JAVA

[JAVA] 람다식 Lambda Expression

jhkimmm 2022. 1. 2. 02:39

JDK1.8 부터 추가된 람다식은 자바를 객체지향언어인 동시에 함수형 언어가 될 수 있게 해주었습니다. 람다식 덕분에 자바는 기존의 자바를 거의 변경하지 않고도 함수형 언어의 장점을 잘 접목시키는데 성공할 수 있었습니다.

 

람다식을 한마디로 표현하자면 "메서드를 하나의 식으로 표현한 것"입니다.

int[] arr = new int[5];
Arrays.setAll(arr, (i) -> (int)(Math.random()*5+1) );

위 코드에서 (i) -> (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. return 값이 있는 메서드의 경우 return문을 식으로 대신할 수 있다. 이때에는 문장 끝에 세미콜론(;)을 붙이지 않는다.

//변환 전
(int a, int b) -> { return a > b ? a : b; }
//변환 후
(int a, int b) -> a > b ? a : b

3. 매개변수의 타입은 추론이 가능한 경우 생략가능하다. (대부분의 경우 생략가능)

//변환 전
(int a, int b) -> a > b ? a : b

//변환 후
(a, b) -> a > b ? a : b

단 "(int a, b) -> a>b?a:b" 와 같이 둘 중 어느 한쪽만 생략하는 것은 허용되지 않습니다.

4. 선언된 매개변수가 하나라면 괄호()를 생략할 수 있다. (단, 타입이 있으면 안된다.)

//변환 전
(int a) -> a + a
//변환 후
a -> a + a
//에러 발생
int a -> a + a

5. 중괄호 {} 안의 문장이 한 줄일 때는 중괄호{}를 생략할 수 있다. (단 return문일 경우는 제외)

//변환 전
(name, age) -> {System.out.println(name + " " +age);}

//변환 후
(name, age) -> System.out.println(name + " " +age)

//에러 발생
(a, b) -> return a > b ? a : b

함수형 인터페이스

자바에서 모든 메서드는 클래스 내에 포함되어야 하는데 람다식은 어디에 포함되어 있을까요?

지금까지 람다식을 메서드 처럼 취급했지만 사실 람다식은 익명 클래스(anonymous class)와 동일하다고 볼 수 있습니다.

예를들어 다음과 같은 인터페이스가 정의되어 있다고 가정했을 때,

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 maxValue = f.max(5, 3);

람다식은 실제로 익명 객체이기 때문에, 다음과 같이 간단하게 익명 객체를 람다식으로 대체할 수 있습니다.

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

이렇게 람다식은 인터페이스를 통해 다루며, 람다식을 다루기 위한 인터페이스를 "함수형 인터페이스(Functional interface)"라고 합니다.

람다식과 인터페이스의 메서드가 1대1로 연결되기 때문에, 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야합니다.

즉 위에서 정의한 MyFunction은 함수형 인터페이스 입니다.

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

함수형 인터페이스를 정의할 때 @FunctionalInterface 어노테이션을 붙여준다면 컴파일러가 함수형 인터페이스를 올바르게 정의하였는지 확인해주므로, 반드시 붙이는 편이 좋습니다.

 

람다식을 참조변수로 다룰 수 있다는 것이 바로 변수처럼 메서드를 주고받는 것을 가능하게 하는 점입니다.

다음과 같이 매개변수나 반환형이 함수형 인터페이스라면, 다음과 같이 람다식의 참조변수를 반환하거나 매개변수로 람다식을 받을 수 있습니다.

// 매개변수의 타입이 함수형 인터페이스라면?
void method1(MyFunction f){
	f.myMethod();
}

method1( (a, b)-> a > b ? a : b );

// 반환형의 타입이 함수형 인터페이스라면?
MyFunction method2(){
	MyFunction f = (a, b) -> a > b ? a : b;
    return f;
}

이렇게 람다식을 사용하면 코드를 간결하고 이해하기 쉽게 작성할 수 있습니다.