람다 정리

Suho·2022년 6월 8일
0

java

목록 보기
2/2

동작 파라미터화

  • 변화하는 요구사항 대응을 위해 사용된다.
  • 요구사항이 변화할 때마다 파라미터를 추가하는 것이 아닌 방법을 찾는 것이 목표

추상화로 필터링

기존 코드는 유지한 채 요구사항을 변경하는 프레디케이드를 구현하는 클래스를 만들면 된다.

(아래의 p가 프레디케이드)

predicate(프레디케이드) : boolean을 반환하는 함수

단점 : 여러 클래스를 구현해야 함. 인스턴스화하는 과정 불편

public static List<Apple> filter(List<Apple> inventory, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
      if (p.test(apple)) {
        result.add(apple);
      }
    }
    return result;
  }
  • 위와 같은 단점을 해결하기 위해 익명 클래스를 사용한다.

익명 클래스

즉석에서 필요한 구현 만들어 사용한다.

단점 : 불필요한 공간 차지, 장황함, 낮은 가독성, 이해하기 어려움

//부모
abstract class Parent {
	abstract String test() {}
}

//인터페이스
interface Human {
	public void humanFunc();
}

//자식(없어도 가능)
abstract class Child extends Parent {
}
new Person() {
	void func() {
				
	}
}.func();
		
Person p = new Person() {
	void func() {
		System.out.println("person");
	}
};
p.func();
		
new Human() {
	public void humanFunc() {
		System.out.println("human");
	}
}.humanFunc();

익명 클래스 사용 시 abstract 하거나 interface 여야 함.

함수형 인터페이스

함수를 1급 객체처럼 다룰 수 있게 함. 인터페이스 선언 & 하나 추상 메서드만 가짐.

람다식은 함수적 인터페이스에서 사용한다.

1급 객체란?

변수나 데이터 구조 안에 담을 수 있다.
파라미터로 전달할 수 있다.
반환값으로 사용할 수 있다.
할당에 사용된 이름과 무관하게 고유한 구별이 가능하다.

사용방법

인터페이스에 선언하여 딱 하나 추상 메서드만을 갖도록 제한

어노테이션 @FunctionalInterface을 사용해서 함수형 인터페이스로 정의 가능

@FunctionalInterface
interface Func{
    int max(int a, int b);
}

public class Lambda {
    public static void main(String[] args) {    
				Func func = (int a, int b) -> a > b ? a : b;
        System.out.println(func.max(3, 5));
    }
}
  • 써서 이득 : 아직 찾지 못함

  • 써서 손해 : 아직 찾지 못함

  • 위의 단점 나열하고 이를 타개하기 위해 람다를 사용한다.

람다

메서드로 전달할 수 있는 익명 함수를 단순화한 것

위에 비해 간단해지고 문제를 더 잘 설명 가능하다.

람다의 특징

  1. 익명
    보통 메서드와 달리 이름 없음.
  2. 함수
    특정 클래스에 종속되지 않음.
  3. 전달
    람다 표현식을 파라미터나 변수로 저장할 수 있음.
  4. 간결성
    익명 클래스보다 덜 구현 가능

문법 정리 & 규칙

1. 기본적인 작성 규칙

  • 이름과 반환 타입은 작성하지 않는다. (anonymous function)
x -> x*x

2. 매개변수(parameter)

  • 추론이 가능한 매개변수의 타입은 생략할 수 있다.
  • 단, 매개변수가 두 개 이상일 경우 일부의 타입만 생략하는 것은 허용되지 않는다.
  • 선언된 매개변수가 하나인 경우 괄호( )를 생략할 수 있다.
  • 단, 매개변수의 타입을 작성한 경우엔 매개변수가 하나라도 괄호( )를 생략할 수 없다.
(x) -> System.out.println(x);
x -> System.out.println(x);
(x,y) -> System.out.println(x + y);
// 안됨!
(int x, y) -> System.out.println(x + y);

3. body { }

  • return 문(return statement) 대신 식(expression)으로 대체할 수 있다.
  • 식(expression)의 끝에 세미콜론(;)은 붙이지 않는다.
  • 괄호{ } 안의 문장이 하나일 때는 괄호{ }를 생략할 수 있다.
  • 이 때, 문장의 끝에 세미콜론(;)은 붙이지 않는다.
  • 그러나 return 문은 괄호를 생략할 수 없다.

() -> System.out.prinln("Hi!");
() -> {}
(int a, int b) -> a > b ? a : b;
(i,j) -> { return i+j; };

람다의 장단점

람다의 장점

  1. 코드 간결함
  2. 가독성 높아짐
  3. 함수 만드는 과정 x → 생산성 높아짐
  4. 병렬 프로그래밍 용이

람다의 단점

  1. 익명 함수 재사용 불가
  2. 디버깅 어려움
  3. 람다 많으면 코드 지저분
  4. 재귀 부적합

*추가

이거 컴파일 안되는 이유

Object o = () -> {
          System.out.println("Tricky example");
  };

Object(대상 형식)이지만 함수형 인터페이스가 아님.

→ Runnable로 대상 형식을 바꿔서 해결 가능

→ Runnable이 lambda의 void 같은 의미

  • Runnable 은 void run() 가짐.

메서드 참조

람다식 아닌 일반 메서드 참조 선언

일반, static, 생성자, 클래스이름::메소드이름 등 java에서 지원함.

Function<Integer, Double> = a -> (Double)a;
BiFunction<Integer, Integer, Double> = (a, b) -> (Double)a/b;

람다 캡처링

람다 내부에서 외부 변수를 수정할 때 에러가 발생

람다식을 작성하면 메모리에서 만들어질 때 stack을 캡처링(복사같은 느낌) 한다. 람다식은 캡처링한 내용을 사용함

  • 문제는 캡처링은 call by reference가 아니라 call by value로 일어남

람다 외부 변수는 참조만 가능하다.

  • 람다 내부에서 외부 컬렉션 조정은 에러 없음. → 컬렉션은 stack영역이 아니라 heap 영역
profile
백엔드 개발자 지망생 :)

0개의 댓글