Last updated
Was this helpful?
Last updated
Was this helpful?
람다 표현식은 메서드로 전달할 수 있는 익명 함수를 단순화한 것이다.
익명 : 보통의 메서드와 달리 이름이 없으므로 익명이라 표현한다.
함수 : 람다는 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 부른다.
전달 : 람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있다.
간경성 : 익명 클래스처럼 많은 자질구레한 코드를 구현할 필요가 없다.
람다의 구성 요소 - 파라미터 리스트 - 화살표 : 파라미터 리스트와 바디를 구분한다. - 바디 : 람다의 반환값에 해당하는 표현식
함수형 인터페이스는 정확히 하나의 추상 메서드를 지정하는 인터페이스이다.
@FunctionalInterface
어노테이션을 붙여 주면 메서드가 2개 이상일 경우 컴파일
단계에서 에러를 발생 시킬 수 있다.
람다 표현식
으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있으므로 전체 표현식을 함수형 인터페이스의 인스턴스로 취급(기술적으로 함수형 인터페이스를 concreate
구현한 클래스의 인스턴스) 할 수 있다.
→ 15장 16장에서 다시 확인
java.util.function.Predicate
인터페이스는 test라는 추상 메서드를 정의하며 test는 제네릭 형식 T의 객체를 인수로 받아 불린을 받환한다.
java.util.function.Consumer
인터페이스는 제네릭 형식 T 객체를 받아서 void
를 반환하는 accept
라는 추상 메서드를 정의한다. T 형식의 객체를 인수로 받아서 어떤 동작을 수행하고 싶을 때 Consumer 인터페이스를 사용할 수 있다.
java.util.function.Function
인터페이스는 제네릭 형식 T를 인수로 받아서 제네릭 형식 R 객체를 반환하는 apply
라는 추상 메서드를 정의한다. 입력을 출력으로 매핑하는 람다를 정의할 때 Function 인터페이스를 활용할 수 있다. 자바 8에서는 기본형
을 입출력으로 사용하는 상황에서 오토박싱
동작을 피할 수 있도록 특별한 버전의 함수형 인터페이스
를 제공한다.
기본형 참조형의 메모리 할당은 어떻게 될까? 오토박싱은 무엇인가?
람다 표현식
자체에는 람다가 어떤 함수형 인터페이스
를 구현하는지의 정보가 포함되어 있지 않다. 따라서 람다 표현식을 더 제대로 이해하려면 람다의 실제 형식을 파악해야 한다.
람다가 사용된 콘텍스트는 무엇인가? 우선 filter
의 정의를 확인하다.
대상 형식은 Predicate
이다.
Predicate
인터페이스의 추상 메서드는 무엇인가?
Apple
을 인수로 받아 boolean
을 반환하는 test
메서드다
함수 디스크립터는 Apple
→ boolean
이므로 람다의 시그니처와 일치한다. 람다도 Apple
을 인수로 받아 boolean
을 반환하므로 코드 형식 검사가 성공적으로 완료된다.
자바 컴파일러
는 람다 표현식이 사용된 콘텍스트
를 이용해서 람다 표현식과 관련된 함수형 인터페이스
를 추론한다. 결과적으로 컴파일러
는 람다 표현식의 파라미터 형식에 접할 수 있으므로 람다 문법
에서 이를 생략할 수 있다.
람다 표현식에서는 익명 함수가 하는 것처럼 자유 변수
(파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수)를 활용할 수 있다. 이를 람다 캡처링
이라고 부른다.
하지만 람다 표현식은 한 번만 할당할 수 있는 지역 변수
를 캡처할 수 있다. (final
로 선언된 변수만 사용 가능)
왜 지역 변수에 이런 제약이 필요할까? 인스턴스 변수는 힙에 저장되는 반면 지역 변수는 스택에 위치한다. 람다에서 지역 변수에 바로 접긍할 수 있다는 가정 하에 람다가 스레드에서 실행된다면 변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서는 해당 변수에 접근하려 할 수 있다. 따라서 자바 구현에서는 원래 변수에 접근을 허용하는 것이 아니라 자유 지역 변수의 복사본을 제공한다. 따라서 복사본의 값이 바뀌지 않아야 하므로 지역 변수에는 한 번만 값을 할당해야 한다는 제약이 생긴 것이다.
람다식은 어느 메모리에 할당되는 것일까?
메서드 레퍼런스는 특정 메서드만을 호출하는 람다의 축약형
이다.
메서드 레퍼런스를 새로운 기능이 아니라 하나의 메서드를 참조하는 람다를 편리게 표현할 수 있는 문법으로 간주 할 수 있다.
코드전달 → 익명 클래스 사용 → 람다 표현식 사용 → 메서드 레퍼런스 사용
Comparator
는 Comparable
키를 추출해서 Comparator
객체로 만드는 Function
함수를 인수로 받는 정적 메서드 comparing
을 포함한다. 그러므로 다음처럼 사용할 수 있다.
메서드 레퍼런스를 이하면 더 깔끔하게 표현도 가능하다.
비교자 구현
을 그대로 재사용하여 사과의 무게를 기준으로 역정렬
할 수 있다.
thenComparing
은 함수를 인수로 받아 첫 번째 비교자를 이요해서 두 객체가 같다고 판단되면 두 번째 비교자에 객체를 전달한다.
Predicate
인터페이스는 복잡한 Predicate
를 만들 수 있도록 negate
, and
, or
세 가지 메서드를 제공한다.
Function
인터페이스는 andThen
, compose
두 가지 디폴트 메서드를 제공한다.
andThen
메서드는 주어진 함수를 먼저 적용한 결과를 다른 함수의 입력으로 전달하는 함수를 반환한다.
compose
메서드는 인수로 주어진 함수를 먼저 실행한 다음에 그 결과를 외부 함수의 인수로 제공한다.
람다 표현식
은 익명 함수
의 일종이다. 이름은 없지만 파라미터 리스트
, 바디
, 반환 형식
을 가지며 예외
를 던질 수 있다.
람다 표현식으로 간결한 코드
를 구현할 수 있다.
함수형 인터페이스
는 하나의 추상 메서드
만을 정의하는 인터페이스다.
함수형 인터페이스를 기재하는 곳에서만 람다 표현식
을 사용할 수 있다.
람다 표현식을 이용해서 함수형 인터페이스의 추상 메서드를 즉석으로 제공할 수 있으며 람다 표현식 전체
가 함수형 인터페이스
의 인스턴스로 취급된다.
자바 8은 Predicate
와 Function
같은 제네릭 함수형 인터페이스와 관련된 박싱 동작을 피할 수 있도록 IntPredicate
, IntToLongFunction
등과 같은 기본형 특화 인터페이스
도 제공한다.
실행 어라운드 패턴
을 람다와 활용하면 유연성
과 재사용성
을 추가로 얻을 수 있다.
메서드 레퍼런스
를 이용하면 기존의 메서드 구현을 재사용하고 직접 전달할 수 있다.
Comparator
, Predicate
, Function
같은 함수형 인터페이스는 람다 표현식을 조합할 수 있는 다양한 디폴트 메서드
를 제공한다.
람다
메서드 레퍼런스 단축 표
(Apple a) → a.getWeight()
Apple::getWeight
() → Thread.currentThread().dumpStack()
Thread.currentThread()::dumpStack
(str, i) ⇒ str.substring(i)
String::substring
(String s) → System.out.println(s)
System.out::println
자바 8 인 액션 3장을 요약한 내용 입니다.