1 minute read

함수 객체를 사용하는 과거 객체 지향 디자인 패턴에는 익명 클래스 (item 24) 면 충분했다. 하지만 이 방식은 코드가 너무 길기 때문에, 함수형 프로그래밍에 적합하지 않다.

아래 코드는 정렬을 위한 비교 함수를 익명 클래스로 구현한 예시이다.

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

람다식

Java 8부터 함수형 인터페이스의 인스턴스를 람다식을 이용해 만들 수 있게 되었다. 이는 익명 클래스와 개념은 비슷하지만, 코드는 훨씬 간결하고 동작이 명확해진다.

아래 코드는 정렬을 위한 비교 함수를 람다식으로 구현한 예시이다.

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

람다에서는 컴파일러가 문맥을 살펴 람다, 매개변수, 반환값의 타입을 추론하므로 타입을 명시해야 코드가 더 명확한 경우를 제외하고, 람다의 모든 매개변수 타입은 생략하는 것이 좋다.

자바에서 람다를 지원하게 된 이후로, 기존에는 적합하지 않았던 곳에도 함수 객체를 사용할 수 있게 되었다.

열거 타입에서 메서드의 동작이 상수마다 달라야 하는 경우, 열거 타입에 인스턴스 필드를 두어 구현(item 34)할 수 있다. 이 경우 람다를 사용하면 코드가 더욱 간결하고 깔끔해진다.

public enum Operation {
	// 람다식으로 변경
	PLUS ("+", (x, y) -> x + y),
	MINUS ("-", (x, y) -> x - y),
	TIMES ("*", (x, y) -> x * y),
	DIVIDE("/", (x, y) -> x / y);
	
	private final String symbol;
	private final DoubleBinaryOperator op;
	
	Operation(String symbol, DoubleBinaryOperator op) {
		this.symbol = symbol;
		this.op = op;
	}    
	
	@Override public String toString() { return symbol; }
	
	public double apply(double x, double y) {        
		return op.applyAsDouble(x, y);    
	}
}

...

public static void main(String[] args) {  
    double x = 10.0;  
    double y = 5.0;  
  
    for (Operation op : Operation.values()) {  
        System.out.printf("%.1f %s %.1f = %.1f%n", x, op, y, op.apply(x, y));  
    }  
}

람다 식으로 표현했을때 코드가 훨씬 간결해진다고 해서 상수별 클래스 몸체를 쓰지 말아야 하는 것은 아니다.

  • 상수별 동작을 간결하게 구현하기 어렵거나, 인스턴스 필드 혹은 메서드를 사용해야만 한다면 상수별 클래스 몸체를 사용하는 것이 좋다.
  • 추상 클래스의 인스턴스를 만들 때 람다 사용이 불가능하므로, 익명 클래스를 사용해야 한다.
  • 람다는 자신을 참조할 수 없으므로 함수 객체가 자신을 참조해야 한다면(this) 익명 클래스를 사용하자.

람다도 익명 클래스처럼 직렬화 형태가 구현별로 다를 수 있으므로, 람다를 직렬화하는 일은 삼가자.

Leave a comment