본문 바로가기

Java

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

이전의 자바

예전에는 자바에서 함수 타입을 표현할 때 추상 메서드를 하나만 담은 인터페이스(드물게는 추상 클래스)를 사용했다.
이런 인터페이스의 인스턴스를 함수객체(function object)라고 하여, 특정 함수나 동작을 나타내는 데 썼다.

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

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

람다의 등장 - 자바 8 이후

자바 8에 와서 추상 메서드 하나짜리 인터페이스는 특별한 의미를 인정받아 특별한 대우를 받게 되었다. 지금은 함수형 인터페이스라 불리는 이 인터페이스들의 인스턴스를 람다식(lambda expression)을 사용해 만들 수 있게 된 것이다.

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

여기서 람다, 매개변수(s1,s2), 반환값의 타입은 각각 Comparator, String, int지만 코드에서는 언급이 없다.

  • 타입을 명시해야 코드가 더 명확할 때만 제외하고는, 람다의 모든 매개변수 타입은 생략하자.

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

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

더 나아가 자바 8, 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; }
  },

  ...

열거 타입에 인스턴스 필드를 두는 방식에서 람다를 이용하면 상수별로 다르게 동작하는 코드를 쉽게 구현할 수 있다.

public enum Operation{
  PLUS("+", (x,y) -> x+y ),
  MINUS("-", (x,y) -> x+y ),

  ...  

람다의 단점

  • 람다는 이름이 없고 문서화도 못한다.
  • 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아지면 람다를 쓰지 말아야 한다.

람다의 한계

람다의 시대가 열리면서 익명 클래스는 설 자리가 크게 좁아진 게 사실이다. 하지만 람다로 대체할 수 없는 곳이 있다.

  • 람다는 함수형 인터페이스에서만 쓰인다. 예컨대 추상 클래스의 인스턴스를 만들 때 람다를 쓸 수 없으니, 익명 클래스를 써야 한다.
  • 비슷하게 추상 메서드가 여러 개인 인터페이스의 인스턴스를 만들 때도 익명 클래스를 쓸 수 있다.
  • 마지막으로 람다는 자신을 참조할 수 없다. 람다에서 this 키워드는 바깥 인스턴스를 가리킨다. 반면 익명 클래스에서의 this는 익명 클래스의 인스턴스 자신을 가리킨다.
  • 람다도 익명 클래스처럼 직렬화 형태가 구현별로 다를 수 있다. 따라서 람다를 직렬화하는 일은 극히 삼가야 한다.