본문 바로가기

Effective Java 3rd/Chapter 7. 람다와 스트림

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

반응형

jdk1.1 부터 익명클래스(Anonymous Class)를 사용했는데, jdk1.8 부터는 람다식을 적용하면서 코드를 간결하게 작성할 수 있게 되었습니다.

람다식은 수학자 알론조(Alonzo Church)가 발표한 람다 계산법에서 사용된 식으로 이를 제자 존 매카시(John Macarthy)가 프로그래밍 언어에 도입했습니다.

사실 이것이 중요한 것은 아니고, Java8 버전부터 람다식(Lambda Expressions)을 지원하게 됨으로서 기존 익명함수(Anonymous function)을 대체하는 방법을 익혀야 한다.

람다식은 익명함수(Anonymous function)을 생성하기 위한 식으로 매개변수를 가진 코드 블록이다. 그리고 런타임 시에는 익명 구현 객체(추상메소드가 한 개인 객체)를 생성한다.

위 설명처럼 람다식이란 일종의 함수형 프로그래밍에 적합한 문법적 표현방식이고, 함수형 프로그래밍은 병렬처리와 이벤트 지향 프로그래밍에 적합하며 등등 장점들을 가지고 있다.

결국 람다식은 로컬 익명 구현객체를 생성하게 되지만, 사실 람다식의 사용 목적은

인터페이스가 가지고 있는 메서드를 간편하게 즉흥적으로 구현해서 사용하는 것

이 목적이다.

 

함수 객체 - Function Object


자바에서는 함수 타입을 표현할 때 추상 메서드를 하나만 담은 인터페이스(또는 추상 클래스)를 사용했습니다.

이러한 인터페이스의 인스턴스 함수 객체(Function Object) 라고 하여 특정 함수나 동작을 나타내는데 사용했습니다.

jdk1.8에서 부터는 @FunctionalInterface 애너테이션이 추가 되면서 함수 타입을 표현할 수 있게 되었습니다. 물론 하나의 추상 메서드를 담고 있는 인터페이스인 것은 동일합니다.

1
2
3
4
5
6
7
8
@FunctionalInterface
public interface Comparator<T> {
    ...
    int compare(T o1, T o2);
    ...
}
 
Collections.sort(words, Comparator.comparingInt(String::length));
cs
Collections.sort(words, Comparator.comparingInt(String::length));

여기서 Comparator는 FunctionalInterface이다.

@FunctionalInterface 애너테이션은 오직 추상 메서드가 하나뿐인 인터페이스에만 달 수 있습니다.

애너테이션 명세에 타깃이 ElementType.TYPE으로 명시되어 있지만 일반적인 애너테이션과는 다른 취급을 받는다. Class, Abstract class, Enum 혹은 추상 메서드가 하나 이상인 인터페이스에 @FunctionalInterface 애너테이션을 달 경우 컴파일 타임에 오류를 잡아낼 수 있다.

 

익명 클래스 - Anonymous Class


 

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

위와 같이 익명 클래스를 생성해서 필요한 메서드를 재정의 할 수 있습니다.

과거 객체 지향 패턴에는 이런 방식이 적절했다. 하지만, 익명 클래스를 사용하는 방식은 코드가 너무 길기 때문에 자바는 함수형 프로그래밍에 적합하지 않았다.

 

람다 - Lambda


그래서 jdk 1.8 버전 부터는 추상 메서드 하나를 가지고 있는 인터페이스, 즉 위에서 설명한 함수형 인터페이스를 람다식(Lambda expression)를 사용해 만들 수 있게 되었습니다.

public static void main(String[] args) {
    List<String> words = Arrays.asList("55555", "333", "1", "4444", "22");

    // 람다 형식
    Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
		
		// List의 sort를 활용한 방식
		words.sort(Comparator.comparingInt(String::length));

    // 익명클래스 형식
    Collections.sort(words, new Comparator<String>() {
        @Override
        public int compare(String s1, String s2) {
            return Integer.compare(s1.length(), s2.length());
        }
    });

    System.out.println(words);
}

위와 같이 짧은 예제로 간결해진 람다식을 확인할 수 있다.

여기서 람다, 매개변수(s1, s2), 반환값의 타입은 코드에 언급되지 않는다. 이것은 컴파일러가 타입 추론을 해준 것이다. 상황에 따라 컴파일러가 타입을 추론을 할 수 없는 경우도 있는데 이때는 프로그래머가 직접 명시해줘야 한다.

반응형