자바가 8로 판올림되면서 작은 함수 객체를 구현하는 데 적합한 람다가 도입되었음. 익명 클래스는 (함수형 인터페이스가 아닌) 타입의 인스턴스를 만들 때만 사용하라. 람다는 작은 함수 객체를 아주 쉽게 표현할 수 있어 (이전 자바에서는 실용적이지 않던) 함수형 프로그래밍의 지평을 열었음
개요
자바 8에 와서 추상 메서드 하나짜리 인터페이스는 특별한 의미를 인정받아 특별한 대우를 받게 되었음. 지금은 함수형 인터페이스라 부르는 이 인터페이스들의 인스턴스를 람다식을 사용해 만들 수 있게 된 것
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()));
- 매개변수 (s1, s2), 반환 값의 타입은 각각 String, String, Comparator<String> 이지만 코드에서는 이에 대한 언급이 없음. 컴파일러가 알아서 문맥을 살펴 타입을 추론해 준 것
타입을 명시해야 코드가 더 명확할 때만 제외하고는, 람다의 모든 매개변수 타입은 생략하자.
- 제네릭의 로 타입을 쓰지 말라고 한 것과 연관이 됨. 로 타입을 사용하게 되면 컴파일러가 알아서 타입 추론을 하지 못하게 됨.
열거 타입에서 람다 사용 예시
public enum Operation { PLUS("+") { 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); }
public enum Operation { PLUS("+", (x, y) -> x+y), ... private final String symbol; private final DoubleBinaryOperator op; public double apply(double x, double y) { return op.applyAsDouble(x,y); } }
- 위의 예시를 보면 상수별 클래스 몸체를 더 이상 사용할 이유가 없다고 느껴질 수 있지만, 꼭 그렇지는 않음
메서드나 클래스와 달리, 람다는 이름이 없고 문서화도 못 함. 따라서 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아지면 람다를 쓰지 말아야 함
- 람다는 한 줄 일 때 가장 좋음
- 또한, 람다는 생성자에 넘겨지기 때문에 인스턴스 멤버에 접근할 수 없다는 단점이 있음. 인스턴스 필드나 메서드를 사용해야 하는 상황이라면 상수별 클래스 몸체를 사용해야 함
익명 클래스 사용 경우
- 추상 클래스의 인스턴스는 람다를 쓸 수 없으니 익명 클래스 사용해야 함
- 람다는 자신을 참조할 수 없음. 람다에서의 this 키워드는 바깥 인스턴스를 가리키고, 익명 클래스에서의 this 는 익명 클래스의 인스턴스 자신을 가리킴. 그래서 함수 객체가 자신을 참조해야 한다면 익명 클래스 사용해야 함
주의점
- 람다도 익명 클래스처럼 직렬화 형태가 구현별로(가령 가상머신별로) 다를 수 있음. 따라서
람다를 직렬화하는 일은 극히 삼가야 함
- 직렬화 해야만 하는 함수 객체가 있다면 private 정적 중첩 클래스의 인스턴스를 사용하자.