람다란 무엇인가?
람다 표현식은 익명 클래스처럼 클래스의 이름이 없고 메서드를 인수로 전달할 수 있으므로 익명 클래스와 유사하다
- 익명: 이름이 없기때문에 익명의 특징을 갖는다
- 함수: 메서드처럼 특정 클래스에 종속된것이 아니기때문에 함수라고 부른다. 하지만 메서드처럼 파라미터, 바디, 반환형식, 가능한 예외 리스트를 포함한다
- 전달: 람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있다
- 간결성: 익명 클래스처럼 많은 자질구레한 코드를 구현할 필요가 없다
Comparator<Apple> byWieght = new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
}
익명 클래스로 구현한 위 코드를 람다를 사용함으로써 아래와 같이 간결하게 표현할 수 있다
Comparator<Apple> byWeight = (Apple a1, Apple a2) ->
a1.getWeight().compareTo(a2.getWeight());
람다를 깊이 이해하기위해서는 함수형 인터페이스 개념을 이해해야 합니다
함수형 인터페이스의 정의는 오직 하나의 추상 메서드만을 가지는 인터페이스를 의미하며 함수형 인터페이스의 특징으로는 아래와 같습니다 - (default 메서드는 제외)
- 단일 추상 메서드: 하나의 추상메서드만 가지며 이 추상 메서드가 함수형 인터페이스의 기능을 정의합니다
@FunctionalInterface
어노테이션: 함수형 인터페이스임을 명확하게 표시하기 위해 사용됩니다 이 어노테이션을 붙이면 컴파일러가 해당 인터페이스에 추상 메서드가 하나만 있는지 검증합니다
자바 표준 라이브러리 속에 함수형 인터페이스의 예시를 들어볼 수 있습니다
Predicate<T>
: 입력 값을 받아서 true
또는 false
를 반환하는 함수
Predicate<String> isEmpty = s -> s.isEmpty();
Function(T, R)
: 입력 값을 받아서 다른 타입의 결과를 반환하는 함수
Function<String, Integer> stringLength = s -> s.length();
Consumer<T>
: 입력 값을 받아서 소비하지만 반환값이 없는 함수
Consumer<String> print = s -> System.out.println(s);
Supplier<T>
: 값을 제공하는 역할을 하는 함수로 인수는 없고 결과를 반환합니다
Supplier<Double> randomValue = () -> Math.random();
이 예시들만 보면 충분히 함수형 인터페이스가 무엇인지 알 수 있을 것 입니다
이름만 어렵지 그냥 단 하나의 추상 메서드를 가지고 있는 인터페이스일 뿐입니다
그렇다면 람다와 함수형 인터페이스는 어떤관계가 있는걸까?
함수형 인터페이스는 단 하나의 추상 메서드를 갖기때문에 람다 표현식과 밀접한 관계를 가지고 있습니다
결론부터 말씀드리자면 람다는 함수형 인터페이스에만 적용할 수 있습니다
이를 정확히 이해하기위해서는 동작 파라미터화라는 개념을 이해해야한다
간단히 설명하자면 동작 파라미터는 말그대로 매개변수에 동작(함수)가 들어가는 것을 의미합니다
빠른 이해를 위해 아래 코드를 살펴보면
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
List<T> result = new ArrayList<>();
for (T e : list) {
if (predicate.test(e)) {
result.add(e);
}
}
return result;
}
먼저 Predicate
역시 함수형 인터페이스로 이는 동작 파라미터를 위한 인터페이스라고 생각하시면 될 것 입니다
Predicate
는 하나의 추상클래스를 갖고 있기때문에 두번째 매개변수인 predicate
는 호출하는 시점에 직접 구현을 해주어야합니다
구현하는 방법에는 익명 클래스나 람다를 사용할 수 있지만 익명 클래스를 사용하게 될 경우 코드가 지저분 해질 수 있기때문에 보통은 람다를 사용하게 됩니다
filter
메서드를 사용하는 예시는 아래와 같습니다
List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0);
(Integer i) -> i % 2 == 0
이라는 동작을 predicate
매개변수에 파라미터화 함으로써 filter
메서드 안에서 어떤 것을 필터링 할 것인지 정의할 수 있게됩니다
이제 본론으로 돌아와서 눈썰미가 있으신분들이라면 지금까지 설명한 글에 대해 어떤 공통점이 있다는 것을 느끼셨을 겁니다
람다는 함수형 인터페이스만 적용될 수 있으며 함수형 인터페이스는 곧 동작 파라미터화를 의미하게 됩니다
즉 람다 표현식은 결국 동작 파라미터화 하기위한 표현식이고 그 표현식을 받아주는 인터페이스가 함수형 인터페이스 입니다
함수형 인터페이스는 말 그대로 인터페이스이기때문에 구현이 존재하지 않습니다
이를 매개변수로 받음으로써 해당 매개변수에는 "구현"이 필요하게 되고 이 때 사용하게 되는 것이 람다 표현식인 것입니다
위의 예시로 표현하자면(Integer i) -> i % 2 == 0
이 코드는filter(List<T> list, Predicate<T> predicate)
이 메서드의 predicate
를 구현하고 있는 것입니다
그렇기때문에 함수형 인터페이스가 하나의 추상클래스로 구성되어있어야 한다는 특징과 깊은 관계를 갖게 되는 것입니다
만약 Predicate<T>
에서 여러 개의 추상클래스를 가지고있다면,(Integer i) -> i % 2 == 0
이 코드는 어떤 추상클래스를 구현해야하는지 알 수 없게되겠죠?
참고로 함수형 인터페이스를 직접 정의할 때 @FunctionalInterface
어노테이션을 사용하면 추상 메서드가 반드시 하나여야만 한다는 조건을 컴파일 단계에서 잡을 수 있게 됩니다
![[Pasted image 20241107041248.png]]
정리하자면 람다는 매개변수를 함수형 인터페이스로 받게하여 매개변수를 구현하고 인스턴스를 생성하도록 하는 방법이다
람다와 컴파일러
그렇다면 컴파일러는 람다를 어떻게 확인하는지 알아보자
List<Apple> heavierThan150g = filter(apples, (Apple a) -> a.getWeight() > 150)
위와 같이 람다를 사용할 경우 컴파일러는 아래의 순서대로 형식을 확인한다
filter
메서드의 선언을 확인filter
메서드는 두 번째 파라미터로Predicate<Apple>
형식을 기대Predicate<Apple>
는test()
라는 하나의 추상 메서드를 정의하는 함수형 인터페이스임을 확인test
메서드는Apple
을 받아서boolean
을 반환하는 것을 확인filter
메서드로 전달된 인수는 이와 같은 요구사항을 만족해야한다
메서드 레퍼런스
메서드 레퍼런스는 람다 표현식보다 가독성이 좋고 자연스럽게 해줄 수 있다
예시를 코드로 살펴보겠습니다
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
inventory.sort(compareing(Apple::getWeight));
위 두 개의 코드는 Apple
의 무게를 통해 정렬하는 기준을 정하는 Comparable
인스턴스를 생성하는 같은 동작을 하는 코드이지만 어떤 코드가 더 가독성이 좋은가요?
Apple
의 getWeight()
로 무게를 조회하여 정렬시킨다는 목적에서 아래의 코드가 더 깔끔하고 가독성이 좋아보입니다.
물론 모든 상황에서 메서드 레퍼런스를 사용하는 것이 좋다고 할 수는 없지만 적절한 때에 람다표현식 대신 메서드 레퍼런스를 사용하여 좋은 코드를 작성할 수 있을 것입니다
'Language > Java' 카테고리의 다른 글
[JAVA] 예외 계층 (0) | 2024.07.09 |
---|---|
[JAVA] 자바의 정렬 인터페이스 Comparable과 Comparator (0) | 2024.07.04 |
해시 알고리즘 (0) | 2024.06.29 |
[JAVA - 자료구조] HashSet (0) | 2024.06.29 |
[JAVA] 불변객체 (0) | 2024.06.24 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!