람다식

람다식은 자바 8버전 이상부터 사용이 가능하며 메소드를 하나의 '식(expression)' 으로 표현한 것이다.

int[] arr = new int[5];
Arrays.setAll(arr, 
		i -> (int)(Math.random() * 5) + 1 // 이 부분이 람다식이다
            );

 

메소드를 람다식으로 표현하면 메서드의 이름과 반환값이 없으므로, 람다식을 '익명 함수(anonymous function)'이라고도 한다.

 

위의 람다식을 메서드로 표현하면 다음과 같다.

int method(){
	return (int)(Math.random() * 5) + 1;
}

메서드 형태보다 람다식이 간결하면서도 이해하기 쉽다.

 

모든 메서드는 클래스에 포함되어야 하므로 클래스도 새로 만들어야 하고, 객체도 생성해야 이 메서드를 호출할 수 있지만, 람다식은 이 과정 없이 오직 람다식 자체만으로 이 메서드의 역할을 수행할 수 있는 것이 큰 장점이다.

 


람다식 작성

// 메소드
반환타입 메소드명(매개변수 선언) {
	문장
}

// 람다식
(매개변수 선언) -> {
	문장
}

메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통{} 사이에 '->' 화살표를 추가한다.

 

유형 1.

// 메서드
int max(int a, int b)}
	return a > b ? a : b
}

// 람다식
(int a, int b) -> {
	return a > b ? a : b
}

 

유형2.

(int a, int b) -> { a > b ? a : b}
  • 반환값이 있는 메서드의 경우 return문 대신 '식'으로 대신할 수 있다.
  • 문장이 아닌 '식'이므로 끝에 세미콜론(;)을 붙이지 않는다.

유형3.

(a, b) -> {a > b ? a : b}
  • 매개변수의 타입은 추론이 가능한 경우 생략할 수 있는데, 대부분의 경우 생략이 가능하다.

유형4.

(a) -> a * a
(int a) -> a * a

a-> a * a // 통과
int a -> a * a // 에러
  • 매개변수가 하나일 때는 괄호를 생략할 수 있다.
  • 단, 매개변수가 타입이 있으면 괄호를 생략할 수 없다.

유형5.

(name, i) -> {
	System.out.println(name + "=" + i);
}

(name, i) -> System.out.println(name + "=" + i)
  • 괄호 {}의 문장이 하나일 때는 괄호 {}를 생략할 수 있다.
  • 괄호 {}의 문장이 return문일 경우 괄호를 생략할 수 없다.

함수형 인터페이스

// 1
(int a, int b) -> a > b ? a : b

// 2
new Object(){
	int max(int a, int b){
    		return a > b ? a : b;
    	}
}

람다식은 메서드와 동등한 것 처럼 보이지만, 사실 람다식은 익명 클래스의 객체와 동등하다.

 

위의 익명 객체의 메서드를 어떻게 호출할 수 있을까?

참조변수가 있어야 객체의 메서드를 호출할 수 있으니 일단 익명 객체의 주소를 f라는 참조변수에 저장한다.

 

타입 f = (int a, int b) -> a > b ? a : b

참조변수 f의 타입으로는 클래스 또는 인터페이스가 가능하다 (참조형이므로)

그리고 람다식과 동등한 메서드가 정의되어 있는 것이어야 한다. 그래야 참조변수로 익명 객체(람다식)의 메서드를 호출할 수 있기 때문이다.

 

예를 들어 max()라는 메서드가 정의된 MyFunction 인터페이스가 있다고 가정하자.

interface MyFunction{
	public abstract int max(int a, int b);
}

위의 인터페이스를 구현한 익명 클래스의 객체는 아래와 같이 생성할 수 있다.

MyFunction f = new MyFunction(){
	public int max(int a, int b){
    		return a > b ? a : b
   	}
};

int big = f.max(5,3) // 익명 객체의 메서드 호출
  • MyFunction 인터페이스에 정의된 메서드 max()는 람다식 '(a,b)->a>b?a:b' 와 메서드 선언부가 일치한다.

따라서 위 코드의 익명 객체를 아래와 같이 람다식으로 변경할 수 있다.

 

MyFunction f = (a,b) -> a > b ? a : b;
int big = f.max(5,3);

 

익명 객체를 람다식으로 대체 가능한 이유

람다식도 실제로는 익명 객체이고, MyFunction 인터페이스를 구현한 익명 객체의 메서드 max()와 람다식의 매개변수 타입과 개수, 그리고 반환값이 일치하기 때문이다.

 

위에서 봤듯이 하나의 메서드가 선언된 인터페이스를 정의해서 람다식을 다루는 것은 기존의 자바 규칙을 어기지 않으면서도 자연스럽다.

 

그래서 인터페이스를 통해 람다식을 다루기로 결정했고, 람다식을 다루기 위한 인터페이스를 함수형 인터페이스라고 부르기로 했다.

 

@FunctionalInterface
interface MyFunction { //함수형 인터페이스 MyFunction을 의미
	public abstract int max(int a, int b);
}
  • 함수형 인터페이스는 오직 하나의 추상 메서드만 존재해야 한다.
    • 그래야만, 람다식과 인터페이스의 메소드가 1:1로 연결될 수 있다.
  • static 메서드와 default 메서드의 개수는 제약이 없다.
 @FunctionalInterface 를 붙이면, 컴파일러가 함수형 인터페이스를 올바르게 정의하였는지 확인하므로, 붙이는 습관을 들이자.

+ Recent posts