[자바] 람다 캡처링

June·2022년 3월 18일
0

우테코

목록 보기
23/84

학습 배경

    public int getBestScore() {
        int sum = 0;

        cardHand.stream()
                .map(Card::getNumber)
                .map(Number::getScore)
                .map(number -> {
                    if (sum > 21) {
                        // Ace가 있는지 탐색해서 ace가 있으면 1로 계산하기
                    }
                })
                .reduce(0, Integer::sum);


        return sum;
    }

합계를 내는데 만약 합계가 21이 넘으면 ace가 있는지 탐색해서 ace를 11대신 1로 계산을 하고자했다.

하지만 아래와 같은 문구가 떴다.

Variable used in lambda expression should be final or effectively final

개념

람다 캡처링은 람다 외부에서 정의된 변수를 람다식 내부에서 사용하는 것이다. 하지만 제약조건이 있다.

인스턴스 변수와 정적 변수는 자유롭게 람다에서 참조할 수 있지만 지역변수는 그렇지 않다. 지역 변수는 final 변수 혹은 실질적으로 final 처럼 사용되는 (즉 재할당이 일어나지 않는) 변수만 사용가능하다.

왜 이럴까?

차이는 어디에 저장되느냐에 있다. 인스턴스 변수는 힙에 저장되고 지역 변수는 스택에 저장된다. 지역 변수는 쓰레드 간에 공유가 되지 않기 때문에 복사본을 사용한다. 따라서 복사본의 값은 바뀌면 안되기 때문에 값의 재할당을 허용하지 않는 것이다.

이러한 제약 변수 덕분에 외부 변수를 변화시키는 일반적인 명령형 프로그래밍과 차이가 난다.

해결

    public int getBestScore() {
        int sum = cardHand.stream()
                .map(Card::getNumber)
                .mapToInt(Number::getScore)
                .sum();

        for (Card card : cardHand) {
            sum = card.getBest(sum);
        }

        return sum;
    }

참고

모던 자바 인 액션 3장
https://bugoverdose.github.io/development/lambda-capturing-and-free-variable/

0개의 댓글