본문 바로가기
Effective Java

[Effective Java] 아이템42 익명 클래스보다는 람다를 사용하라

by byeongoo 2021. 6. 20.

■ 익명 클래스와 람다 비교

예전에는 자바에서 함수 타입을 표현할 때 추상 메서드를 하나만 담은 인터페이스를 사용했다. 익명 클래스의 인스턴스를 함수 객체로 사용하는 것은 낡은 기법이다. 익명 클래스 방식은 코드가 너무 길기 때문에 자바는 함수형 프로그래밍에 적합하지 않았다.

Collection.sort(words, new Comparator<String>() {
  public int compare(String s1, String s2){
    return Integer.compare(s1.length(), s2.length());
  }
});

 

지금은 함수형 인터페이스라 부르는 이 인터페이스들의 인스턴스를 람다식을 사용해 만들 수 있게 됐다. 다음은 익명 클래스를 사용한 앞의 코드를 람다 방식으로 바꾼 모습이다. 위의 코드 보다 훨씬 간결해졌다.

Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));

 

위의 코드에서 특별한 것은 타입에 대한 언급이 없다는 것이다. 컴파일러가 문맥을 살펴보고 타입을 추론해준다. 상황에 따라 컴파일러가 타입을 결정하지 못할 수도 있는데 그럴 때는 프로그래머가 직접 명시해야한다.

 

타입을 명시해야 코드가 더 명확할 때만 제외하고는, 람다의 모든 매개변수 타입을 생략하자.

 

자바8 때 List 인터페이스에 추가된 sort 메서드와 비교자 생성 메서드를 사용하면 더 간단히 만들 수 있다.

words.sort(comparingInt(String::length));

 

■ 열거타입에서의 람다

아이템 34에서는 상수별 클래스 몸체를 구현하는 방식보다는 열거 타입에 인스턴스 필드를 두는 편이 낫다고 했다. 람다를 이용하면 열거 타입의 인스턴스 필드를 이용하는 방식으로 상수별로 다르게 동작하는 코드를 쉽게 구현할 수 있다.

import java.util.function.DoubleBinaryOperator;

enum Operation {

    PLUS("+", (x, y) -> x+y),
    MINUS("-", (x, y) -> x-y),
    TIMES("*", (x, y) -> x*y),
    DIVDE("/", (x, y) -> x/y);

    private final String symbol;
    private final DoubleBinaryOperator op;

    Operation(String symbol, DoubleBinaryOperator op) {
        this.symbol = symbol;
        this.op = op;
    }

    @Override
    public String toString() {
        return symbol;
    }

    public double apply(double x, double y){
        return op.applyAsDouble(x, y);
    }
}

 

람다 기반 Operation 열거 타입을 보면 상수별 클래스 몸체는 더 이상 사용할 이유가 없다고 느낄 수 있다. 하지만 람다는 이름이 없고 문서화도 못한다. 따라서 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아지면 람다를 쓰지 말아야한다.

 

■ 람다가 대체 할 수 없는 것

  1. 추상클래스의 인스턴스를 만들때 람다사용은 불가능하다.
  2. 추상메서드가 여러개인 인터페이스의 인스턴스도 람다로 표현 불가능하다.
  3. 람다의 this는 바깥 인스턴스를 가리킨다.

■ 유의 사항

직렬화가 필요한 경우 람다와 익명 클래스를 삼가해야한다. vm마다 직렬화 형태가 다르기 때문이다. 직렬화 해야하는 함수 객체는 private 정적 중첩 클래스(Comparator처럼)의 인스턴스로 구현하자.