익명 클래스보다는 람다를 사용하라
익명 클래스
Collections.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()));
위 코드는 추상 메서드(compare) 하나만 담은 Comparator 클래스를 익명 클래스로 구현하고 있다. 이 방식은 코드가 너무 길어 함수형 프로그래밍에 적합하지 않았다.
아래 코드는 자질구레한 코드들이 사라지고 어떤 동작을 하는지 명확히 드러내고 있다. 특히 타입 추론을 이용해 간결하게 작성하고 있는데, 타입을 명시해야 코드가 더 명확할 때만 제외하고는 매개변수 타입은 생략하는 것을 추천한다.
비교자 생성 메서드를 사용해 간결하게 만들 수 있으며,
Collections.sort(words, comparingInt(String::length));
List 인터페이스에 추가된 sort 메서드를 이용해 더 간결하게 표현할 수 있다.
words.sort(comparingInt(String::length));
public enum Operation {
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 symbol; }
// 추상 메서드
public abstract double apply(double x, double y);
}
각 enum에 apply 함수를 구현한 예제(아이템 34)다. 이때 상수별 클래스 몸체를 구현하기보다는 인스턴스 필드를 두는 것이 낫다고 하였는데, 후자의 방식으로 람다를 구현할 수 있다.
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 double apply(double x, double y) {
return op.applyAsDouble(x, y);
}
}
람다는 이름이 없고 문서화를 할 수 없기에 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아지면 지양하는 것이 좋다. (1~3 줄 안에 끝내는 것이 좋다)
enum 타입에서 사용하는 람다의 인수는 컴파일 시점 타입추론이 되므로 필드에 접근할 수 없다. 즉, 필드나 메서드를 사용해야할 일이 있다면 상수별 클래스 몸체를 사용해야 한다.
1. 추상 클래스의 인스턴스를 만들 때 익명 클래스
abstract class AbstractClass {
public abstract void abstractMethod();
}
AbstractClass abstractInstance = new AbstractClass() {
@Override
public void abstractMethod() {
System.out.println("Implemented abstractMethod");
}
};
추상 클래스는 멤버 변수, 메서드를 가지는데 람다식은 변수에 접근할 수 없으므로 람다로는 사용할 수 없다.
2. 추상 메서드가 여러 개인 인터페이스의 인스턴스를 만들 때 익명 클래스
마찬가지로 함수형 인터페이스(추상 메서드 하나)가 아닌 인터페이스를 람다로 생성할 수 없으므로 익명클래스 사용
3. 자신을 참조해야 할 경우 익명 클래스
람다의 this는 바깥 인스턴스를 가리키고, 익명 클래스의 this는 자신을 참조하는 것이다.