본문 바로가기

Java/Java SE, EE

[Java/Java SE, EE] final과 non-final, 그리고 effective-final의 차이

final과 non-final

final 키워드는 초기화된 변수의 값을 수정할 수 없도록 불병성(immutable)을 부여합니다. 

final int var = 100;

// final 변수는 값을 변경 불가
var = 200;

non-final은 자바 프로그래밍에서 제공하는 키워드는 아닙니다. 단순히 final 키워드를 사용하는 변수의 반대 의미로써, 일반 변수를 뜻합니다.

자바에서 non-final 기본(Primitive) 타입의 지역 변수는 스택 메모리에서 관리됩니다. 어떤 메소드에서 지역 변수를 선언하면, 메소드가 종료되는 시점에 지역 변수는 스택 메모리에서 제거됩니다.

반면 final 기본 타입의 지역 변수는 non-final과 달리 JVM 상수 풀에서 관리됩니다. 메소드에서 final 지역 변수를 선언하면, non-final과 생명 주기를 달리 합니다.

effective-final

자바의 람다(lambda)와 익명(annonymous) 객체는 외부의 매개 변수와 지역 변수에 접근 할 수 있습니다. 예시는 익명 객체 Calculatable을 선언하고 있는 메소드의 매개 변수(_arg0, _arg1)와 지역 변수(localVar0, localVar1)의 합을 반환합니다.

static interface Calculatable {
   int sum();
}

static class AnnonymousExample {
   void method(int _arg0, int _arg1) {
      int localvar0 = 5;
      int localVar1 = 10;
      
      Calculatable calc = new Calculatable() {
         @Override
         public int sum() {
            return _arg0 + _arg1 + localvar0 + localVar1;
         }
      };
      
      System.out.println(calc.sum());
   }
}

메소드의 매개 변수(_arg0, _arg1)와 지역 변수(localVar0, localVar1)는 메소드가 종료되는 시점에 스택 메모리에서 제거됩니다. 반면 익명 객체는 참조(Reference) 타입이므로 힙 메모리에서 관리됩니다. 힙 메모리에서 관리되는 익명 객체는 스택 메모리에서 관리되는 매개 변수 및 지역 변수와 생명 주기를 달리합니다.

익명 객체는 외부의 매개 변수 및 지역 변수를 참조 할 수 있으나, 서로 간에 생명 주기가 다릅니다. 이미 스택 메모리에서 제거된 매개 변수와 지역 변수를 익명 객체에서 여전히 참조할 수 있다는 문제가 있습니다. 때문에 익명 객체가 참조하는 외부 변수는 final 키워드를 사용하여 JVM 상수 풀에서 관리하도록 해야 합니다.

static interface Calculatable {
   int sum();
}

static class AnnonymousExample {
   // 매개 변수를 final로 선언
   void method(final int _arg0, final int _arg1) {
      // 지역 변수를 final로 선언   
      final int localvar0 = 5;
      final int localVar1 = 10;
      
      Calculatable calc = new Calculatable() {
         @Override
         public int sum() {
            return _arg0 + _arg1 + localvar0 + localVar1;
         }
      };
      
      System.out.println(calc.sum());
   }
}

하지만 자바 8부터는 effective-final 기능을 제공하여 람다 또는 익명 객체가 참조하는 외부 변수가 non-final인 경우 컴파일러가 자동으로 final 키워드를 추가하기 때문에 별도로 final 키워드를 지정할 필요가 없습니다.

  • 자바 7 이하
    람다 또는 익명 객체가 참조하는 외부 변수에 final 키워드 사용
  • 자바 8 이상
    effective-final을 제공하여 람다 또는 익명 객체가 참조하는 외부 변수에 자동으로 final 키워드 추가