-
Java 항해일지 - 15. 람다식카테고리 없음 2021. 3. 6. 00:19
목표
자바의 람다식에 대해 학습하세요.
학습할 것 (필수)
- 람다식 사용법
- 함수형 인터페이스
- Variable Capture
- 메소드, 생성자 레퍼런스
람다란 무엇인가?
람다 표현식이란 메서드로 전달할 수 있는 익명 함수를 단순화 한 것.
람다의 특징 4가지
- 익명: 메서드 이름 없이 사용 가능해 메서드 이름 짓는 것에 대한 고민이 덜어진다.
- 함수: 특정 클래스에 종속되지 않는다.
- 전달: 람다 표현식 자체를 메서드의 인수로 전달하거나 변수로 저장할 수 있다.
- 간결성: 익명클래스보다 간결하다
람다표현식의 구성
( 람다 파라미터 ) -> { 람다 바디 }
- 표현식 스타일
- (parameters) -> expression
- 블록 스타일
- (parameters) -> { statements; }
람다 사용법
함수형 인터페이스
함수형 인터페이스는 하나의 추상 메서드만을 정의하는 인터페이스이다. 우테코 로또 미션에서 만들었던 로또 생성 인터페이스를 예시로 들 수 있다.
public interface LottoGenerator { int MIN_LOTTO_NUMBER = 1; int MAX_LOTTO_NUMBER = 45; int FROM_INDEX = 0; Lotto generate(); // 추상 메서드가 오직 하나 }
람다 표현식을 통해 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있어 람다 표현식 자체를 함수형 인터페이스의 인스턴스로 취급할 수 있다. 마찬가지로 로또 미션에서 예시를 가져왔다.
public static Lotto ofRandomLotto() { // 람다 표현식으로 변경한 예제 LottoGenerator randomGenerator = () -> new Lotto(Arrays.asList(1,2,3,4,5,6)); return randomGenerator.generate(); } public void makeRandomLottos(int autoAmount) { for (int i = 0; i < autoAmount; i++) { lottos.add(Lotto.ofRandomLotto()); } }
함수 디스크립터
함수 디스크립터란 람다 표현식의 시그니처를 서술하는 메서드이다. Predicate, Consumer, Function, Supplier 등이 있다.
- Predicate
제네릭 형식 T의 객체를 인수로 받아 불리언을 반환한다.
Predicate<String> startsWithSeoul = (str) -> str.startsWith("Seoul"); System.out.println(startsWithSeoul.test("SeoulCity")); // true 출력
- Consumer
제네릭 형식 T의 객체를 인수로 받아 void를 반환하는 accept라는 추상 메서드를 정의한다.
Consumer<String> printCity = (city) -> System.out.println(city); printCity.accept("Seoul");
- Function
제네릭 형식 T의 객체를 인수로 받아서 제네릭 형식 R 객체를 반환하는 추상 메서드 apply를 정의한다.
Function<Integer, Integer> plus10 = (number) -> number + 10; System.out.println(plus10.apply(10)); // 20 출력
그 외 다양한 함수 디스크립터
- () -> T : Suuplier
- T -> T : UnaryOperator
- (T, T) -> T : BinaryOperator
- (T, U) -> T : BiPredicate<L, T>
- (T, U) -> void : BiConsumer<T, U>
- (T, U) -> R : BiFunction<T, U, R>
지역 변수 사용
람다는 인스턴스 변수와 정적 변수를 자유롭게 캡처할 수 있으나 지역 변수는 명시적으로 final로 선언되어 있어야 하거나, final로 선언된 변수와 똑같이 사용되어야 한다. 다시 말해 한 번만 할당할 수 있는 지역 변수를 캡처(외부에서 정의된 변수를 사용하는 것)할 수 있다.
메서드 레퍼런스
메서드 참조를 이용하면 람다 표현식의 인수를 더 깔끔히 전달할 수 있다.
- 정적 메서드 참조 (Integer::parseInt)
- 다양한 형식의 인스턴스 메서드 참조 (String::length)
- 기존 객체의 인스턴스 메서드 참조 (LottoNumber::getNumber)
생성자 레퍼런스
ClassName::new와 같이 클래스명과 new 키워드를 이용해 기존 생성자의 참조를 만들 수 있다. 정적 메서드의 참조를 만드는 방법과 비슷하다.
람다 표현식을 조합할 수 있는 유용한 메서드
Comparator 조합
reversed(), thenComparing() 등을 조합해 사용할 수 있다.
inventory.sort(comparing(Apple::getWeight) .reversed() // 역으로 정렬 .thenComparing(Apple::getCountry)); 두 사과의 무게가 같으면 국가별 정렬
Predicate 조합
negate(), or(), and() 등을 조합해 사용할 수 있다.
redApple.and(apple -> apple.getWeight() > 150) .or(apple -> GREEN.equals(apple.getColor())); // 빨간 사과 중 무게가 150 넘는 사과 혹은 초록 사과 (오른쪽 연결)
Function 조합
andThen(), compose() 등을 조합해 사용할 수 있다.
Function<Integer, Integer> plus10 = (number) -> number + 10; Function<Integer, Integer> multiply3 = (number) -> number * 3; Function<Integer, Integer> multiply3AndPlus10 = plus10.compose(multiply3); System.out.println(multiply3AndPlus10.apply(4)); // 결과 22 Function<Integer, Integer> plus10AndMultiply3 = plus10.andThen(multiply3); System.out.println(plus10AndMultiply3.apply(4); // 결과 42
정리
- 람다 표현식은 익명 함수의 일종이다. 이름만 없을 뿐 파라미터 리스트, 바디, 반환형식을 가지며 예외도 던질 수 있다.
- 함수형 인터페이스는 하나의 추상 메서드만을 정의하는 인터페이스이다.
- 람다 표현식 전체가 함수형 인터페이스의 인스턴스로 취급된다.