지금까지 깃이나 구글에서 다른 사람이 쓴 코드를 참고할 때, 람다식 문법을 사용한 것을 적지 않게 볼 수 있었다. 생각해보면, 나는 람다식을 다른 사람의 코드를 참고하는 것이 아닌 직접 코드를 쓸 때 사용한 적이 한 번도 없었다. 아마 람다식이 왜 사용되는지, 어떤 점이 유용한지 제대로 모르고 있기 때문일 것이다. 그래서 이번에는 람다식에 대해 제대로 알아보려고 한다.
람다식이란 함수를 하나의 식으로 표현한 것이다. 함수를 람다식으로 표현하면 메소드의 이름이 필요 없기 때문에, 람다식은 익명 함수의 한 종류라고 볼 수 있다.
익명함수(Anonymous Function)란 함수의 이름이 없는 함수로, 익명함수들은 모두 일급 객체이다.
일급 객체란 일반적으로 다를 객체들에 적용 가능한 연산을 모두 지원하는 개체를 가르킨다. 함수를 값으로 사용 할 수도 있으며 파라메터로 전달 및 변수에 대입 하기와 같은 연산들이 가능하다.
람다식을 사용한 간단한 예를 들겠다.
public String hello() {
return "Hello World!";
}
위 함수를 람다식으로 표현하면,
() -> {return "Hello World!";}
과 같이 간단한 식으로 표현할 수 있다.
() 안에는 함수에 전달할 매개 변수가 들어간다.
장점
단점
여기까지가 람다식에 대한 개념적인 부분이다. 이제 람다식을 사용하기 위해서는 함수형 인터페이스가 무엇인지 알아야 한다.
함수형 인터페이스
함수형 인터페이스란 함수를 1급 객체처럼 다룰 수 있게 해주는 어노테이션으로, 인터페이스에 선언하여 단 하나의 추상 메소드만을 갖도록 제한하는 역할을 한다. 함수형 인터페이스를 사용하는 이유는 Java의 람다식이 함수형 인터페이스를 반환하기 때문이다.
함수형 인터페이스를 구현하기 위해서는 인터페이스를 개발하여 그 내부에는 1개 뿐인 abstract 함수를 선언하고, 위에는 @FunctionalInterface 어노테이션을 붙여주면 된다.
@FunctionalInterface
interface LambdaFunc {
int max(int a, int b);
}
public class Lambda {
public static void main(String[] args) {
LambdaFunc lambdaFunc = (int a, int b) -> a > b ? a : b;
System.out.println(lambdaFunction.max(3, 5));
}
}
여기서 놓치지 말아야 하는 것은 람다식으로 생성된 순수 함수는 함수형 인터페이스로만 선언이 가능하다는 점이다. 또한 @FunctionalInterface는 해당 인터페이스가 1개의 함수만을 갖도록 제한하기 때문에, 여러 개의 함수를 선언하면 컴파일 에러가 발생한다.
이번에는 내가 썻던 코드 중 제대로 알지도 못한 채 무작정 썻던 람다식 하나를 다시 한번 살펴보겠다.
UserServiceException result= assertThrows(UserServiceException.class,()->
basketService.basketToMyIngredient(userId,ingredName));
여기서 asserThrows 메서드의 원형을 보면,
public static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable) {
return AssertThrows.assertThrows(expectedType, executable);
}
위와 같이 선언되어 있다.
그리고, assertThrows 메서드에서 람다식을 사용한 두번째 매개변수인 Executable 클래스의 원형을 살펴보면,
@FunctionalInterface
@API(status = STABLE, since = "5.0")
public interface Executable {
void execute() throws Throwable;
}
위와같이 함수형 인터페이스가 선언되어 있는것을 확인할 수 있었다.
이를 확인 한후, 내가 썻던 코드를 다시 보니 assertThrows 메서드는 Exception 클래스와 함수형 인터페이스인 Executable 인터페이스를 매개변수를 가졌고, 두번째 매개변수는 함수형 인터페이스이기 때문에 람다식으로 직접 익명함수를 만들어주었던 것이다. 그리고 그 결과로 throw 된 Exception을 반환한다.
이렇게 람다식에 대해 정리해보았다.
완벽하게 이해된 것은 아니지만, 적어도 내가 쓰고있는 코드가 어떤 의미인지는 알고 사용할 수 있을 것 같다. 그리고, 위에서 람다식의 장점 중 병렬처리에 용이하다는 항목이 있었는데, 이는 Java8부터 지원하는 Stream과 관련된 내용이다. 이 부분은 또 다음에 다시 포스팅 하도록 하겠다.
참고자료