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

신명철·2022년 3월 9일
0

Effective Java

목록 보기
40/80

함수형 인터페이스

과거에는 함수 타입을 표현할 때 추상 메서드를 하나만 담은 인터페이스를 사용했다. 이런 인터페이스의 인스턴스를 함수 객체라고 해서, 특정 함수나 동작을 나타내는 데 사용했다. JDK1.1이 등장하면서 이 함수 객체를 만드는 주요 수단은 익명 클래스가 되었다. 다음 코드를 예로 들어보자.

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

익명 클래스는 코드가 너무 길기 때문에 함수형 프로그래밍에 적합하지 않았기에 자바 8 부터는 함수형 인터페이스라 부르는 이 인터페이스의 인스턴스를 람다식으로 만들 수 있게 되었다.

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

람다의 매개변수에는 타입에 대한 언급이 없다. 이는 컴파일러가 타입 추론 규칙에 의해 타입을 추론해줬기 때문이다. 타입 명시 규칙은 너무 복잡하기 때문에 타입을 명시해야 코드가 더 명확해지는 경우에만 제외하고는 람다의 매개변수 타입을 생략하도록 하자.

컴파일러가 타입을 추론할 때 필요한 타입 정보 대부분을 제네릭에서 얻기 때문에 람다는 제네릭을 사용할 때 두배로 중요해진다. 우리가 타입 정보를 제공하지 않으면 컴파일러는 람다의 타입을 추론할 수 없게 되어 결국 우리가 일일이 명시해야 한다.

람다 자리에 비교자 생성 메서드를 사용한다면 이 코드를 더 간단하게 만들 수 있다.

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

더 나아가 자바8 때 List 인터페이스에 추가된 sort 메서드를 사용하면 더욱 짧아진다.

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

람다를 쓰지 말아야 하는 경우

람다는 메서드나 클래스와는 달리, 이름이 없고 문서화도 하지 못한다. 따라서 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아지면 람다를 쓰지 말아야 한다. 람다는 한 줄일 때 가장 좋고, 길어도 세 줄을 넘기면 안된다. 가독성이 나빠지기 때문이다.

열거 타입 생성자에 주어지는 인수들의 타입들은 컴파일 타임에 추론된다. 따라서 열거 타입 생성자 안의 람다는 열거 타입의 인스턴스 멤버에 접근할 수 없다(인스턴스는 런타임에 만들어지기 떄문이다). 따라서, 상수별 동작을 단 몇줄로 구현하기 어렵거나 인스턴스 필드나 메서드를 사용해야 하는 경우에는 상수별 클래스 몸체를 사용해야 한다.

람다로 대체할 수 없는 경우

람다는 함수형 인터페이스에서만 사용된다. 추상 클래스의 인스턴스를 만들 때는 람다를 사용할 수 없기 때문에 익명 클래스를 사용해야 한다. 비슷하게, 추상 메서드가 여러 개인 인터페이스의 인스턴스를 만들 때도 익명 클래스를 쓸 수 있다.

마지막으로, 람다는 자신을 참조할 수 없다. 람다에서의 this키워드는 바깥 인스턴스를 가리킨다. 반면 익명 클래스의 this는 익명 클래스의 인스턴스 자신을 가리킨다. 그래서 함수 객체가 자신을 참조해야 한다면 반드시 익명 클래스를 써야한다.

람다도 익명 클래스처럼 직렬화 형태가 구현별로 다를 수 있다. 따라서, 람다를 직렬화 하는 것은 극히 삼가해야 한다(익명 클래스의 인스턴스도 마찬가지다). 직렬화해야만 하는 함수 객체가 있다면(Comparator 처럼) private 중첩 클래스[아이템24]의 인스턴스를 사용하자.

profile
내 머릿속 지우개

0개의 댓글