파이널 지역, 람다

류한선·2025년 6월 20일

실기연습-2

목록 보기
17/95

좋은 질문이에요!
"지역 변수의 final"과 "effectively final"은 겉보기는 비슷하지만 엄밀히 따지면 개념적으로 완전히 다르다는 말, 핵심을 짚어서 아주 차근차근 설명해드릴게요.


1. 지역 변수의 final 키워드

(1) 역할:

  • 변수를 한 번 초기화한 뒤에 다시 값을 변경하지 못하도록 '변경 불가' 상태로 만드는 것
  • 즉, final이 붙으면 변수에 값을 한 번만 대입할 수 있고, 그 이후에는 재할당(=값 변경)이 불가능해집니다.

(2) 사용 위치:

  • 지역 변수 선언 시 (메서드 내에서) 직접 final 키워드를 붙입니다.

(3) 예시 코드:

void example() {
    final int x = 10;  // final로 선언된 지역 변수
    // x = 20; // 오류! final 변수이므로 재할당 불가
}

(4) 컴파일러 입장:

  • final 키워드가 붙으면, 컴파일러가 해당 변수에 재할당하는 모든 시도를 바로 에러로 잡아냅니다.
  • 즉, 코드 작성 시점에서 확실히 '변경 불가'임을 보장하는 문법적 약속이자 제약 조건입니다.

2. 지역 변수의 "effectively final"

(1) 역할:

  • 변수에 final 키워드를 명시적으로 붙이지 않았지만, '사실상' 한 번 초기화 후 재할당되지 않아 변경 불가 상태가 되는 경우를 말합니다.
  • 즉, final 키워드는 없지만 컴파일러가 보기에 재할당이 없으므로 사실상 final과 동일한 취급을 받는 상태입니다.

(2) 사용 배경:

  • Java 8 이후 람다, 익명 클래스, 내부 클래스 등에서 지역 변수를 사용할 때,
  • 명시적으로 final 선언 없이도, 변수 값이 바뀌지 않는 한 final 변수처럼 안전하게 사용할 수 있도록 도입된 개념입니다.

(3) 예시 코드:

void example() {
    int y = 10;  // final 키워드 없음
    // y = 20;  // 만약 이렇게 재할당하면 effectively final이 아님

    Runnable r = () -> {
        System.out.println(y);  // y는 effectively final이기 때문에 람다에서 사용 가능
    };
}
  • 만약 y가 이후에 재할당되면 컴파일 에러 발생합니다.

(4) 컴파일러 입장:

  • 컴파일러가 코드 흐름을 분석해서,
  • "이 변수는 최초 할당 이후에 값이 한 번도 변경되지 않았다"면, 이 변수는 effectively final로 간주합니다.
  • 그래서 람다 같은 곳에서 final 변수처럼 안전하게 사용 가능하도록 허용합니다.

3. 두 개념 차이 요약

구분지역 변수의 final지역 변수의 effectively final
키워드final 명시적 사용키워드 없음 (컴파일러가 분석해 판단)
컴파일 타임 강제 여부재할당 시 컴파일 에러재할당이 있으면 컴파일 에러, 없으면 허용
코드 작성자 의도 명시 여부명시적으로 "변경 금지" 의도 표현의도는 없지만 사실상 값 변경이 없는 경우
주로 쓰이는 맥락어디서나 쓰임 (일반 지역 변수)람다, 익명 클래스에서 지역 변수 사용 시
코드 유지보수성코드 읽을 때 '이 변수 변경 없다' 확실히 알 수 있음키워드 없어서 바로 보이진 않지만 변경 없음

4. 왜 이런 차이가 생겼을까?

  • Java 7 이전에는 람다 없고 익명 클래스에서 지역 변수를 참조하려면 반드시 final 붙여야 했어요.
  • Java 8 람다 도입 시, 불필요한 final 선언 강제는 불편해서, 컴파일러가 대신 분석해 "실제로 값이 안 바뀌면 final 처럼 쓰도록 허용"하는 effectively final 개념을 도입했습니다.
  • 그래서 **final은 '명시적 선언', effectively final은 '컴파일러가 추론한 상태'**라고 이해하면 쉬워요.

5. 간단한 비유로 이해하기

  • final 변수 = "여기 붙어 있으니 절대 움직이지 마라!"라고 큰 글씨로 경고문 붙여놓은 상태
  • effectively final 변수 = "움직이지는 않는데, 경고문이 없어서 너 혼자 조심해야 함" (컴파일러가 다 봐주긴 함)

6. 예제: 둘의 차이와 효과 확인

void test() {
    final int a = 5;   // 명시적 final
    int b = 10;        // effectively final (초기값 이후 변경 없음)
    
    Runnable r = () -> {
        System.out.println(a);  // OK
        System.out.println(b);  // OK (effectively final)
    };
    
    // a = 6; // 오류! final 변수 재할당 불가
    // b = 15; // 오류! effectively final 아니게 됨 -> 람다에서 사용 불가
}

결론 정리

  • final 키워드 붙인 변수는 반드시 재할당 금지, 코드에서 명확히 알 수 있음
  • effectively final은 키워드 없지만, 컴파일러가 "재할당 없는 변수"로 판단하여 final처럼 취급함
  • 그래서 둘은 쓰임새와 선언 방식이 다르지만, 람다나 내부 클래스 내에서 지역 변수를 사용할 때는 final이거나 effectively final이어야만 한다는 점에서 중요하게 구분됩니다.

좋아요! 문제 풀이를 아주 상세하게, 한 줄 한 줄 설명하듯 해볼게요.


문제 코드 다시 보기

public class FinalTest {

    public void testMethod() {
        final int x = 10;
        int y = 20;

        Runnable r1 = () -> {
            System.out.println("x = " + x);
            System.out.println("y = " + y);
        };

        // y = 30;  // (A) 이 줄의 주석을 해제하면 어떻게 될까요?

        System.out.println("Before run");

        r1.run();

        System.out.println("After run");
    }

    public static void main(String[] args) {
        new FinalTest().testMethod();
    }
}

질문 1 답변

질문 1

(A) 줄의 주석이 해제되지 않은 상태에서는 컴파일 오류가 발생할까요? 발생한다면 이유는 무엇인가요?


풀이

  • x 변수는 final int x = 10;으로 명시적으로 final 선언되어 있습니다.
    → 그래서 x절대 재할당이 불가능한 변수입니다.
    → 람다 안에서 사용할 때 문제없음.

  • y 변수는 int y = 20;으로 final 키워드 없이 선언됐지만, 이후에 값이 변경되지 않음effectively final 상태입니다.
    → 즉, 값이 최초 할당(20) 이후에 변경되지 않아, 컴파일러가 final처럼 인식합니다.

  • 람다 표현식에서 지역 변수 참조 시, 참조하는 변수는 반드시 final이거나 effectively final이어야 함 (Java 문법 규칙).

  • (A) 주석이 해제되어 있지 않으면 y는 변경되지 않고 그대로 20을 유지하기 때문에,
    람다 내에서 y를 사용하는 데 컴파일 오류가 발생하지 않습니다.


정리:

(A) 주석 해제 안 한 상태에서는 x는 final, y는 effectively final이므로 둘 다 람다에서 문제 없이 참조 가능하다.
→ 따라서 컴파일 오류는 발생하지 않는다.


질문 2 답변

질문 2

(A) 줄의 주석을 해제한 상태에서는 컴파일 오류가 발생할까요? 발생한다면 이유는 무엇인가요?


풀이

  • (A) 줄을 해제해서 y = 30;로 하면, y가 값이 변경됩니다.

  • 이 경우, y더 이상 effectively final이 아닙니다.

  • 람다 내 지역 변수 참조 규칙은,
    람다 내에서 사용하는 지역 변수는 final이거나 effectively final이어야 한다는 점입니다.

  • 하지만 yfinal 키워드가 없고, 값이 변경되었으므로 effectively final 조건도 만족하지 않습니다.

  • 따라서 컴파일러는 람다 내 y 참조 부분에서 컴파일 오류를 발생시킵니다.

  • 구체적으로,

    • 람다 생성 시점에서 y가 변경될 가능성이 있기 때문에,
    • 람다에서 사용할 지역 변수의 값이 불변임을 보장하지 못해 안전하지 않다고 판단하는 것입니다.

정리:

(A) 주석 해제하면 y가 재할당되어 effectively final 조건을 깨트리므로, 람다에서 y를 참조하는 부분에서 컴파일 오류 발생한다.


질문 3 답변

질문 3

x 변수는 final로 선언되어 있고, y 변수는 final 키워드가 없는데도 람다 내에서 정상적으로 사용됩니다.
y도 람다에서 사용 가능할까요?


풀이

  • x는 명시적 final 변수이므로 당연히 람다에서 사용할 수 있습니다.

  • yfinal 키워드가 없지만, 선언 이후 재할당 없이 값이 변경되지 않았기 때문에
    → 컴파일러가 이를 effectively final로 판단합니다.

  • Java 8부터, 람다 내부에서 사용하는 지역 변수는 반드시 final이거나 effectively final이어야만 한다고 규칙이 바뀌었고,

  • 그래서 final 키워드를 꼭 붙이지 않아도 변수가 변경되지 않으면 람다 내에서 사용할 수 있도록 허용되었습니다.

  • effectively final컴파일러가 분석해 결정하는 특성이기 때문에, 코드에는 키워드가 없지만 final과 동일하게 취급되는 것입니다.


정리:

yfinal 키워드는 없지만, 재할당하지 않아 effectively final로 인정되어 람다에서 사용 가능하다.


추가 설명: 왜 람다에서 지역 변수가 final 또는 effectively final이어야 할까?

  • 람다는 지역 변수의 복사본을 참조하기 때문입니다.
  • 지역 변수의 값이 바뀌면 람다 내부 값과 실제 변수 값이 달라져 혼란이 생깁니다.
  • 그래서 값을 변경하지 않는다는 보장을 하여 안전하게 사용할 수 있도록,
  • final 또는 effectively final이어야 한다는 제한을 둔 것입니다.

전체 요약

상황컴파일 오류 발생 여부이유
(A) 주석 해제 안 함오류 없음y가 effectively final 상태임
(A) 주석 해제 함오류 발생y가 재할당되어 effectively final 아님
x 사용항상 오류 없음명시적 final 변수이기 때문

0개의 댓글