반응형

ITEM 42 "익명 클래스보다는 람다를 사용하라"

 

Java 8 이전에는 자바에서 함수 타입을 표현할 때 추상 메서드를 하나만 담은 인터페이스를 사용했었다. 이런 인터페이스의 인스턴스를 함수 객체라고 하는데 다른 곳에서 절대 사용하지 않는 가벼운 함수 객체라면 함수 객체를 인자로 받는 공간에 바로 new 연산자를 통해 생성할 수 있었다.

 

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

 

이렇게 인터페이스 (위 예제에서는 Comparator 인터페이스) 를 바로 new 연산자를 통해 사용하고, 구체적인 전략은 익명 클래스 내부에 작성함으로써 전략 패턴의 장점도 사용할 수 있다. 하지만 매우 간단한 코드임에도 불구하고 매우 길다.

 

Java 8 부터는 추상 메서드 하나짜리 인터페이스는 람다식으로 간결하게 작성할 수 있다.

 

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

 

 

매개변수와 반환값에 대한 타입은 각각 String 과 Int 이지만 생략할 수 있다. 컴파일 타임 때 컴파일러가 맥락을 파악하고 자동으로 타입을 추론해주기 때문에 가능하다. 간혹 컴파일러가 타입을 추론하지 못해 컴파일 오류가 발생할 수도 있지만, 그럴 때 직접 프로그래머가 명시하면 된다. 타입을 명시해야 코드가 명확한 경우를 제외하고 람다의 모든 매개변수 타입은 생략하자.

 

더보기

제네릭에서 타입에 대한 정보를 얻을 수 있기 때문에 람다를 사용하는 함수들에서 제네릭을 적극적으로 사용하는 것을 권장한다. 로타입으로 구성했다면 컴파일 오류가 나서 람다 사용하는 부분에서 타입 변환을 해야한다.

 

메서드 참조를 사용하면 더 간결하게 할 수도 있다.

 

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

 

더 나아가 List 인터페이스에 추가된 sort 메서드를 이용하면 더 간결해진다.

 

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

 

아래와 같이, 열거 타입에서 상수 별 추상 메서드를 직접 구현하기 보다는 인스턴스 필드를 인자로 전달받아 간결하게 구현할 수도 있다. (람다로 구현하니 더 깔끔해진다)

 

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;
    
    Operation(String symbol, DoubleBinaryOperator op) {
    	this.symbol = symbol;
        this.op = op;
    }
    
    @Override
    public String toString() { return symbol; }
    
    public abstract double apply(double x, double y) {
    	return op.applyAsDouble(x, y);
   	};
}

 

위 코드에서 사용한 DoubleBinaryOperator 는 java.util.function 패키지가 제공하는 인터페이스 중 하나로 double 타입 인수 2개를 받아 double 타입 결과를 돌려주는 인터페이스이다.

 

간결하게 작성할 수 있다는 가장 큰 장점을 가지고 있는 람다도 사용 시 유의할 사항이 존재한다.

첫 번째, 람다는 이름이 없기 때문에 문서화하기가 곤란하다. 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아진다면 람다를 쓰지 말아야 한다.

두 번째, 인스턴스로 만들어서 재사용할 때 람다를 쓸 수 없다. 익명 클래스를 사용해야 한다. 람다는 태생적으로 한 개의 추상 메서드를 가진 추상 클래스이므로 추상 메서드가 여러 개인 인터페이스의 인스턴스로도 활용이 불가능하다.

세 번째, 익명 클래스에서 this 지시자를 통해 자기 자신의 인스턴스를 참조하는 것과 다르게 람다에서 this 지시자는 람다 바깥 인스턴스를 참조한다.

네 번째, 람다도 익명 클래스처럼 역직렬화/직렬화 형태가 가상머신 VM 마다 구현이 다를 수 있어 유의해야 한다. private 정적 중첩 클래스의 인스턴스를 사용하는 것을 권장한다.

 

"꼭 익명 클래스를 사용해야 하는 자리가 아니라면 익명 클래스보다 간결하게 작성할 수 있는 람다를 사용해보자"

반응형

+ Recent posts