<aside> 💡

자바 8에서 함수형 인터페이스, 람다, 메서드 참조라는 개념이 추가되면서 함수 객체를 더 쉽게 만들 수 있게 되었다. 이와 함께 스트림 API까지 추가되어 데이터 원소의 시퀀스 처리를 라이브러리 차원에서 지원하기 시작했다. 이번장에서는 이 기능들을 효과적으료 사용하는 방법을 알아보자.

</aside>

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

예전에는 함수 타입을 표현할 때 추상 메서드를 하나만 담는 인터페이스(드물게는 추상 클래스)를 사용했다. 이런 인터페이스의 인스턴스를 함수객체(function object)라고 하여, 특정 함수나 동작을 나타내는 데 썼다. 1997년 JDK 1.1이 등장하면서 함수 객체를 만드는 주요 수단은 익명 클래스였다.

다음 코드를 예로 살펴보자.

public static void main(String[] args) {
    List<String> words = Arrays.asList("A","CCC","BB");
    System.out.println("정렬 전: " + words);

		//문자열을 길이순으로 정렬, 익명 클래스의 인스턴스를 함수 객체로 사용
    Collections.sort(words, new Comparator<String>() {
        public int compare(String o1, String o2) {
            return Integer.compare(o1.length(), o2.length());
        }
    });

    System.out.println("정렬 후: " + words);
}

이 코드에서 Comparator 인터페이스가 정렬을 담당하는 추상 전략을 뜻하며, 문자열을 정렬하는 구체적인 전략을 익명 클래스로 구현했다. 하지만 익명 클래스 방식은 코드가 너무 길기 때문에 자바는 함수형 프로그래밍에 적합하지 않았다.

자바 8에 와서 추상 메서드 하나짜리 인터페이스는 지금의 함수형 인터페이스라 부르는 인터페이스들의 인스턴스를 람다식(lambda expression)을 사용해 만들 수 있게 되었다. 람다는 함수나 익명 클래스와 개념은 비슷하지만 코드는 훨씬 간결하다.

위의 예제 코드를 람다식으로 바꿔보자.

Collections.sort(words, (o1, o2) -> Integer.compare(o1.length(), o2.length()));

매개변수의 반환값 타입의 언급은 없어도 컴파일러가 문맥을 살펴 타입을 추론해준다. 타입을 명시해야 코드가 더 명확할 때만 제외하고는, 람다의 모든 매개변수 타입은 생략하자. 컴파일러가 타입을 추론하는 데 필요한 타입 정보 대부분은 제네릭에서 얻는다.

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

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

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

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