Effectively final이란?
final로 선언되지는 않았지만, 초기화된 이후 참조가 변경되지 않아 결과적으로 final처럼 동작하는 변수
자바의 익명클래스나 람다 표현식에서 식 외부에 있는 지역 변수를 참조할때는 반드시 final이거나 선언 후 참조가 변경되지 않는 effectively final이어야한다.
아래와 같이 final이 아닌 count를 익명 클래스에서 참조하면 다음과 같은 에러가 발생한다.
그렇다면 final은 알겠는데 Effectively Final은 무엇일까?
다음 조건을 만족하는 지역변수를 effectively final이라고 한다.
- final로 선언되지 않았다.
- 초기화 진행 후 다시 할당되지 않았다.
- 전위, 후위에 증감 연산자가 존재하지 않는다. (++등)
객체의 경우엔, 객체가 가르키는 참조가 변경되지 않으면 된다. 따라서, 객체의 상태가 변경되어도 참조가 변경되지 않으면 effectively final이다.
그렇다면 왜 Effectively final 또는 final이어야하는지 알기 위해 람다의 동작과정을 알아보자.
람다가 외부에서 정의 변수를 사용할때는 내부에서 바로 가져다 사용하는 것이 아닌 복사본을 만들어 사용한다.
이를 Lambda Capturing이라고 하는데 여기서 말한 외부 변수란 지역변수를 포함하 클래스변수, 인스턴스 변수를 의미한다.
그렇다면 왜 복사본을 만들까?
외부 변수가 지역변수인 경우를 생각해보자. 지역 변수는 메모리의 Stack 영역에 할당된다. 해당 영역은 스레드만의 고유 영역을 가져 다른 스레드와 공유할 수 없고, 스레드가 종료되면 Stack영역 또한 사라진다.
복사를 하지 않는 경우를 생각해보자.
특정 외부 메서드의 스레드와 별도의 스레드에서 실행되는 람다가 해당 메서드의 지역 변수를 참조하고 있을때, 외부 메서드의 스레드가 람다의 스레드보다.먼저 종료되면 람다는 외부 메서드의 지역변수를 더이상 참조할 수 없게된다. 따라서 복사가 필요하다.
그렇다면 복사를 하는데! 왜 지역변수의 값 변경은 불가능할까?
지역 변수를 컨트롤 하는 외부 스레드A가 있고, 람다의 스레드B가 있다고 가정하자.
A에서 지역변수를 선언하고, B에서 값을 복사한다할때 변경 가능한 지역변수라면 B가 값을 복사하는 시점에서 해당 값이최신 값인지 알 수 없다. 또한, 스레드의 고유 특성으로 인해 B스레드에서 A를 참조하는 것 또한 불가능하다.
우선 인스턴스, 클래스변수를 정의하면 다음과 같다.
인스턴스 변수: 클래스에 선언된 변수 인스턴스가 초기화될때 Heap 영역에 할당된다.
클래스변수: 클래스에 선언된 static 변수를 의미하며 인스턴스 초기화 없이 바로 사용이 가능하며 Method 영역에 초기화된다.. 클래스마다 하나씩 밖에 존재하지 않는다.
따라서 별도의 Stack영역에 스레드별로 생성하지 않기에 복사하는 과정이 불필요하다.
즉, 지역 변수가 스택 영역에 생성되어 스레드별로 고유한 작업을 처리하고 스레드는 독립적으로 처리되기에 람다식 내부에서 외부 지역변수를 참조할때는 반드시 Effectively Final이나 final 변수여야한다.
'PL > 모던 자바 인 액션' 카테고리의 다른 글
모던 자바 인 액션 스터디 - 3주차 (0) | 2023.08.23 |
---|---|
HashMap의 동작 구조 (0) | 2023.08.16 |
자바 컬렉션 프레임워크 (0) | 2023.08.15 |
모던 자바 인 액션 스터디 - 2주차 (0) | 2023.08.15 |
모던 자바 인 액션 스터디 - 1주차 PDF (0) | 2023.08.09 |