반응형
이 포스팅에서 작성하는 내용은 EFFECTIVE JAVA(이펙티브자바) 에서 발췌하였습니다.
아이템 42. 익명 클래스보다는 람다를 사용하라
익명 클래스
ex 1) 익명 클래스의 인스턴스를 함수 객체로 사용(예전 기법)
Collection.sort(words, new Comparator<String>() {
public int compare(String s1, String s2){
return Integer.compare(s1.length(), s2.length());
}
});
- 전략 패턴처럼 함수 객체를 사용하는 과거 객체 지향 디자인 패턴에는 익명 클래스면 충분했다.
- 이 코드에서 Comparator 인터페이스가 정렬을 담당하는 추상 전략을 뜻하며, 문자열을 정렬하는 구체적인 전략을 익명 클래스로 구현한 것이다.
- 익명클래스 방식은 코드가 너무 길기 때문에 자바는 함수형 프로그래밍에 적합하지 않다.
람다식
- 자바 8에 와서 추상 메서드 하나짜리 인터페이스는 특별한 의미를 인정받아 특별한 대우를 받게 되었다.
- 함수형 인터페이스라 부르는 이 인터페이스들의 인스턴스를 람다식을 사용해 만들 수 있게 된 것이다.
- 람다는 함수나 익명 클래스와 개념은 비슷하지만 코드는 훨씬 간결하다.
ex 2) 람다식을 함수 객체로 사용(위의 익명 클래스 대체)
Collection.sort(words, (s1,s2) -> Integer.compare(s1.length(), s2.length()));
- 여기서 람다, 매개변수, 반환값의 타입은 각각 (Comparator<String>), String, int지만 코드에서는 언급이 없다.
- 우리 대신 컴파일러가 문맥을 살펴 타입을 추론한 것이다.
- 상황에 따라 컴파일러가 타입을 추론하지 못할 수도 있는데, 이럴 때는 프로그래머가 직접 명시해야한다.
- 타입을 명시해야 코드가 더 명확할 때의 경우를 제외하고 람다의 모든 매개변수 타입은 생략하자.
- 컴파일러가 타입을 알 수 없다고 오류를 나타낼 때 명시해주면 된다.
- 람다 자리에 비교자 생성 메서드를 사용하면 이 코드를 더 간결하게 만들 수 있다.
- Collections.sort(words, comparingInt(String::length));
열거 타입에서의 람다
ex 3) 상수별 클래스 몸체와 데이터를 사용한 열거 타입(아이템 34의 Operation 열거 타입을 예)
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);
}
ex 4) 위의 코드에서 함수 객체(람다)를 인스턴스 필드에 저장해 상수별 동작을 구현한 열거 타입
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);
}
}
- 람다를 이용하면 열거 타입의 인스턴스 필드를 이용하는 방식으로 상수별로 다르게 동작하는 코드를 쉽게 구현할 수 있다.
- 단순히 각 열거 타입 상수의 동작을 람다로 구현해 생성자에 넘기고, 생성자는 이 람다를 인스턴스 필드로 저장해둔 후, apply 메서드에서 필드에 저장된 람다를 호출하기만 하면 된다.
람다의 단점
- 메서드나 클래스와 달리 람다는 이름이 없고 문서화도 못한다. 즉, 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아지면 사용하지 말아야 한다.
- 람다는 함수형 인터페이스에서만 쓰인다. (추상클래스의 인스턴스를 만들 때 람다를 쓸 수 없으니 익명 클래스를 사용해야 함)
- 람다는 자신을 참조할 수 없다. 람다에서의 this 키워드는 바깥 인스턴스를 가리키는 데, 익명 클래스에서의 this는 익명 클래스의 인스턴스 자신을 가리킨다.
- 람다도 익명 클래스처럼 직렬화 형태가 구현별로 다를 수 있기 때문에, 직렬화는 극히 삼가해야 한다.
정리
- 익명 클래스는 함수형 인터페이스가 아닌 타입의 인스턴스를 만들 때만 사용하자
- 람다는 작은 함수 객체를 아주 쉽게 표현할 수 있어 가독성이 좋다.
반응형
'BE > Java' 카테고리의 다른 글
[Effective Java] 아이템 44. 표준 함수형 인터페이스를 사용하라 (0) | 2022.11.03 |
---|---|
[Effective Java] 아이템 43. 람다보다는 메서드 참조를 사용하라 (0) | 2022.11.02 |
[Effective Java] 아이템 41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라 (0) | 2022.10.31 |
[Effective Java] 아이템 40. @Override 애너테이션을 일관되게 사용하라 (0) | 2022.10.28 |
[Effective Java] 아이템 39. 명명 패턴보다 애너테이션을 사용하라 (0) | 2022.10.27 |