[Java] 람다 타입 검사/추론/제약
타입 검사
람다가 사용되는 context를 이용해서 람다의 형식(type)을 추론할 수 있다.
어떤 context에서 기대되는 람다 표현식의 형식을 대상 형식(target type)이라고 부른다.
타입 검사 과정
다음 예시를 통해 타입 검사 과정을 확인해보자.
filter(inventory, (Apple apple) -> apple.getWeight() > 150);
- filter 메서드의 선언을 확인한다.
- filter 메서드는 두 번째 파라미터로 Predicate<Apple> 형식(target type)을 기대한다.
- Predicate<Apple>은 test라는 한 개의 추상 메서드를 정의하는 함수형 인터페이스이다.
- test 메서드는 Apple을 받아 boolean을 반환하는 함수 디스크립터를 묘사한다.
- filter 메서드로 전달된 인수는 이와 같은 요구사항을 만족해야 한다.
대상 형식이라는 특징 때문에 같은 람다 표현식이라도 호환되는 추상메서드를 가진 다른 함수형 인터페이스로 사용될 수 있다.
예를 들어, Callable과 PrivilegedAction 인터페이스는 인수를 받지 않고 제네릭 형식 T를 반환하는 함수를 정의한다.
따라서 다음 코드는 모두 유효하다.
Callable<Integer> c = () -> 42;
PrivilegedAction<Integer> c = () -> 42;
타입 추론
자바 컴파일러는 람다 표현식이 사용된 context(대상 형식)를 이용해서 람다 표현식과 관련된 함수형 인터페이스를 추론한다. 즉, 대상 타입을 이용해서 함수 디스크립터를 알 수 있으므로 컴파일러는 람다의 시그니처도 추론할 수 있다.
결과적으로 컴파일러는 람다 표현식의 파라미터 타입에 접근할 수 있으므로 람다 문법에서 이를 생략할 수 있다. 즉, 다음 처럼 코드를 단순화해도 람다 파라미터 타입을 추론할 수 있다.
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
상황에 따라 타입을 명시하는 것이 좋을 때도 있고 배제하는 것이 좋을 때도 있다. 이는 상황에 맞게 정해서 사용하면 된다.
지역 변수 사용
람다 표현식은 익명 함수가 하는 것처럼 자유 변수를 활용할 수 있는데, 이를 람다 캡처링(capturing lambda)이라고 한다.
여기서 자유 변수는 파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수를 말한다.
다음은 람다 캡처링 예제이다.
int number = 123;
Runnable r = () -> System.out.println(number);
여기서 자유 변수에 제약이 있는데, 람다가 인스턴스 변수와 정적 변수를 자유롭게 캡처할 수 있으려면 지역변수가 명시적으로 final로 선언되어 있거나 실질적으로 final로 선언된 변수와 똑같이 사용되어야 한다.
즉, 람다 표현식은 한 번만 할당할 수 있는 지역 변수를 캡처할 수 있다.
다음 코드는 컴파일 할 수 없다.
int number = 123;
Runnable r = () -> System.out.println(number);
number = 1234;
지역 변수 제약
왜 이런 제약이 필요할까? 우선 인스턴스 변수와 지역 변수의 차이를 알아야 한다.
인스턴스 변수는 힙에 저장되는 반면에 지역 변수는 스택에 위치한다.
람다에서 지역 변수에 바로 접근할 수 있다는 가정하에 람다가 스레드에서 실행된다면 변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서는 해당 변수에 접근하려 할 수 있다.
따라서, 자바 구현에서는 원래 변수에 접근을 허용하는 것이 아니라 자유 지역 변수의 복사본을 제공한다.
여기서 복사본의 값이 바뀌지 않아야 하므로 지역 변수에는 한 번만 값을 할당해야 한다는 제약이 있는 것이다.
또 지역 변수 제약 때문에 외부 변수를 변화시키는 일반적인 명령형 프로그래밍 패턴에 제동을 걸 수 있다.
'Language > Java' 카테고리의 다른 글
[Java] 스트림(Stream) (0) | 2023.04.06 |
---|---|
[Java] 메서드 참조 (0) | 2023.04.04 |
[Java] 함수형 인터페이스 (0) | 2023.04.03 |
[Java] 람다 표현식 소개 (0) | 2023.03.31 |
[Java] 동작 파라미터화 코드 전달하기 (0) | 2023.03.28 |
댓글
이 글 공유하기
다른 글
-
[Java] 스트림(Stream)
[Java] 스트림(Stream)
2023.04.06 -
[Java] 메서드 참조
[Java] 메서드 참조
2023.04.04 -
[Java] 함수형 인터페이스
[Java] 함수형 인터페이스
2023.04.03 -
[Java] 람다 표현식 소개
[Java] 람다 표현식 소개
2023.03.31