Item42. 익명 클래스보다 람다를 사용하라

  • 함수 객체(function object) : 추상 method를 하나만 담은 interface 의 instance (JDK1.1이전)
  • 익명 class : Jdk 1.1이 등장하면서 함수 객체를 만드는데 사용됨.
1
2
3
4
5
6
7
// 익명 class 예시 
Collections.sort(words, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
});

익명 class의 단점으로 추상 method를 하나만 구현하는데, 코드가 길어진다는 단점이 존재한다.

Lambda expression

  • Functional Interface : 추상 method 를 하나만 가지고 있는 interface
1
2
3
4
5
6
@FunctionalInterface
public interface FI {
void doSomething();
// default method는 있어도 상관없음
default void print(String str){ System.out.println("str = " + str); }
}
  • Lambda expression, Lambda : Function interface의 객체를 간결하게 구현할 수 있도록 지원해줌
  • 람다식을 이용하면 기존에 길었던 익명 class 코드를 줄일 수 있다. (paramter 의 type 추론도 지원해줌.)
1
Collections.sort(words,(s1,s2)->Integer.compare(s1.length(),s2.length()));

주의사항 : compiler가 람다식의 매개변수 타입을 추론할때, generic raw type 을 사용하면 추론이 불가능해짐으로, 꼭 타입매개변수를 적어주어야 함.

method reference 의 종류는 다음과 같다, 다음 Item 43에서 자세한 종류를 확인할 수 있다.

  • Static 메소드 레퍼런스
  • Instance 메소드 레퍼런스
  • Constructor 메소드 레퍼런스
1
2
//  static method reference 사용 
Collections.sort(words,comparingInt(String::length));
  • Item34에서 소개했던 Enum 상수값별로 동작이 달라질떄 Enum type내 추상 method를 정의하고 상수별로 class 몸체를 만드는 방식도 람다를 적용하면 더 간결하게 표현이 가능하다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public enum Operation {
// class 몸체 구현
PLUS("+"){ public double apply(double x, double y) { return x+y;}},
MINUS("-"){ public double apply(double x, double y) { return x-y;}},
TIMES("*"){ public double apply(double x, double y) { return x*y;}},
DIVIDE("/"){ public double apply(double x, double y) { return x/y;}};

private final String symbol;

Operation(String symbol){
this.symbol = symbol;
}

@Override
public String toString(){
return this.symbol;
}
// 상수별 동작이 달라지는 추상 method
public abstract double apply(double x, double y);

람다를 적용하면 다음과 같이 간결해진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public enum Operation {
PLUS("+", (x,y) -> x+y ),
MINUS("-",(x,y) -> x-y),
TIMES("*" , (x,y) -> x*y),
DIVIDE("/", (x,y) -> x/y);
private final String symbol;
private final DoubleBinaryOperator op;
// DoubleBinaryOperator 는 double 타입 인수 2개를 받아, double 타입 인수 1개를 반환해주는 함수형 인터페이스이다. ㅂ
Operation(String symbol , DoubleBinaryOperator op){
this.symbol = symbol;
this.op = op;
}
}

Lambda 사용 주의점

  • 이름이 없으며, 문서화가 불가능하다 따라서 동작이 명확하지 않거나, 코드가 길어지면 람다를 쓰지 않는것을 권고한다.

  • 함수형 인터페이스에만 적용가능함으로, 그 외에는 익명class를 사용해야 한다.

  • this가 자기 자신이 아닌, 바깥 인스턴스를 가르킨다. 반면 익명 class는 this가 자기 자신을 가르킨다.

  • Jvm 구현체별로 직렬화 형태가 다를 수도 있으니, 직렬화는 하면 안된다.

  • 외부에 있는 값을 변경하는 등 상태을 가지지 않도록 final 또는 상태를 변경하지 않는게 보장된 effectively final 이 아니면 compile error를 던져준다.

외부 상태에 의존하지 않는 다는 내용은 함수형 프로그래밍 개념의 순수 함수 개념과 관련이 있다.

  1. 순수 함수 (pure function ) : 함수 외부에 있는 값을 변경하지 못하는 등 상태가 없어 사이드 이펙트를 만들 수 없다.

이외에도 함수형 프로그래밍의 주요 특징은 아래와 같다.

  1. 고차 함수 (high-order function ) : 함수가 함수를 매개변수로 받을 수 있고 함수를 리턴할 수도 있다.
  2. 일급객체 (first-class object) : 함수를 일급 객체로 사용할 수 있다, 즉 함수를 변수에 할당하고, 인자로 전달할 수 있다.
  3. 불변성 (Immutable)

Comments