람다의 형식 검사, 형식 추론, 제약
- Coding/Java
- 2020. 11. 6.
도입
람다로 함수형 인터페이스의 인스턴스를 만들 수 있다. 하지만 람다 표현식 자체에는 어떤 함수형 인터페이스를 구현하는지의 정보가 포함되어있지 않으므로 람다의 실제 형식을 제대로 파악해야한다.
형식 검사
람다가 사용되는 context를 이용해서 람다의 형식을 추론할 수 있다. 어떤 context에서 기대되는 람다 표현식의 형식을 대상 형식이라고 부른다. 예를 들면 람다가 전달된 메서드 파라미터나 람다가 할당되는 변수에서 추론하는 것이다.
List<Height> heightThan160cm = filter(inventory, (Height height) -> height.getHeight() > 160);
위 예제는 아래 순서로 형식 확인 과정을 거친다.
(1) filter 메서드의 선언을 확인한다.
(2) filter 메서드는 두번째 파라미터로 Predicate<Height> 형식(대상 형식)을 기대한다.
(3) Predicate은 test라는 한개의 추상 메서드를 정의하는 함수형 인터페이스다.
(4) test 메서드는 Height를 받아 boolean을 반환하는 함수 디스크립터를 묘사한다.
(5) filter 메서드로 전달된 인수는 이와 같은 요구사항을 만족해야한다.
형식 추론
자바 컴파일러는 람다 표현식이 사용된 콘텍스트 (대상 형식)을 이용해서 람다 표현식과 관련된 함수형 인터페이스를 추론한다. 또한 대상형식을 이용해서 함수 디스크립터를 알 수 있으므로 람다의 시그니처도 추론할 수 있다. 결과적으로 컴파일러는 람다 표현식의 파라미터 형식에 접근할 수 있으므로 람다 문법에서 이를 생략할 수 있다.
List<Apple> greenApples = filter(inventory, apple -> GREEN.equals(apple.getColor()));
// 형식을 추론하고있지않음
Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
// type을 생략하여 형식을 추론
Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
위 예제처럼 자바 컴파일러는 람다 파라미터 형식을 추론할 수 있다. 상황에 따라 명시적으로 형식을 포함할때와 포함하지 않을때를 구분하여 코드의 가독성을 향상시키는 것이 좋다.
지역변수의 사용
결론을 먼저 말하자면, 람다에서는 불변한 지역변수만 사용이 가능하다. 불변한 지역변수는 final로 선언되거나 또는 final로 선언된 변수와 똑같이 사용되어지는 변수이다. 단 한번만 할당할 수 있는 지역변수를 사용할 수 있다.
int test = 1;
Runnable r = () -> System.out.println(test); // Error
test = 2;
위 예제에서 지역변수 test는 final로 정의되어있지 않으며 값을 1에서 2로 변경하는 코드도 존재한다. 따라서 final 처럼 사용되어지는 변수도 아니기 때문에 람다에서 사용된 test 코드에서 에러가 발생하는 것이다.
그렇다면, 왜 람다에서는 지역변수의 사용에 제약이 있을까?
내부적으로 인스턴스 변수와 지역 변수는 다르다. 인스턴스 변수는 힙에 저장되고, 지역 변수는 스택에 저장된다. 람다에서 지역 변수에 바로 접근할 수 있다는 가정을 했을때 람다가 스레드에서 실행된다면 변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서는 해당 변수에 접근하려고 할 수 있다. 자바 구현에서는 원래 변수에 접근을 허용하는 것이 아닌 자유 지역 변수의 복사본을 제공한다. 따라서, 복사본의 값이 바뀌지 않아야 하므로 지역 변수에는 한번만 값을 할당한다라는 제약이 생긴것이다.
'Coding > Java' 카테고리의 다른 글
스트림의 기본개념 (0) | 2020.11.06 |
---|---|
람다의 메서드 참조 (0) | 2020.11.06 |
함수형 인터페이스 사용 (0) | 2020.11.06 |
빌더 패턴의 권장 이유 (0) | 2020.11.06 |
실행 어라운드 패턴 (0) | 2020.11.06 |