우리는 앞서 로컬 클래스에서의 사용 제한과 관련해, 로컬 클래스에서 메서드의 파라미터나 로컬 변수를 사용하는 경우의 제약 사항에 대해서 학습했습니다. 잘 기억이 나지 않는다면, 'final' 키워드가 힌트가 되겠습니다.
익명 객체에서도 마찬가지 입니다.
익명 객체 내부에서는 바깥 클래스의 필드 / 메서드를 제약 없이 사용이 가능합니다. 하지만, 익명 객체 내부에서 "메서드의 파라미터나 로컬 변수를" 사용하게 되면 문제가 발생하게 됩니다. 왜 문제가 발생할까요? 위의 아티클에서 설명한 내용과 동일합니다.
일단 "메서드 내에서 선언된 익명 객체는 메서드 실행이 끝나도 힙(heap) 메모리에 남게 되어" 계속 사용이 가능합니다. 하지만 해당 메서드의 파라미터와 로컬 변수는 메서드 실행이 끝나는 순간 당연히 스택(stack)에서 사라지게 됩니다. 그래서 해당 익명 객체에서 메서드의 파라미터나 로컬 변수를 사용한다면 에러가 발생하는 구조가 됩니다.
이 문제의 해결 방식은 로컬 클래스의 사용제한 규격과 동일합니다. 로컬 클래스는 클래스의 이름이 있고, 익명 객체 인스턴스는 말 그대로 이름이 없다는 차이점만 존재합니다. 해당 메서드의 파라미터나 로컬 변수는 익명 객체 내부로 복사되어서 사용하게 됩니다.
익명 객체 내부에서 매서드의 파라미터 혹은 로컬 변수를 사용하게 되면, 이 변수들은 자동으로 final 특성을 갖게 됩니다. 이 역시 자바 7까지는 final을 명시적으로 선언해야 했지만, 자바 8부터는 final을 명시적으로 선언하지 않아도 자동으로 final의 특성을 갖게 됩니다. 단, final을 명시적으로 선언했을 경우에는 해당 변수가 복사되어 저장하는 위치가 달라집니다. 이 역시 로컬 클래스에서의 처리 방식과 동일합니다. 즉, final 키워드가 있다면 메서드 내부의 로컬 변수로 복사되며 final 키워드가 없다면 익명 클래스의 필드 영역으로 복사됩니다.
void outMethod(final int arg1, int arg2) {
final int var1 = 1;
int var2 = 2;
인터페이스 변수 = new 인터페이스() {
void method() {
int result = arg1 + arg2 + var1 + var2;
}
};
}
위의 예제를 기준으로 보겠습니다. 여기서 익명 객체에서 사용되는 변수는 arg1, arg2, var1, var2입니다. 여기서 arg1과 var1은 final 키워드가 있는 상태입니다. 그리고 arg2와 var2는 final 키워드가 없는데, 이 코드가 실행되면 실제로는 아래와 같은 형태로 익명 객체 인스턴스에 각각의 값을 저장하게 됩니다.
인터페이스 변수 = new 인터페이스() {
int arg2 = 파라미터값;
int var2 = 2;
void method() {
int arg1 = 파라미터값;
int var1 = 1;
int result = arg1 + arg2 + var1 + var2;
}
물론 final 키워드 유무에 따른 복사 위치를 아주 정교하게 외울 필요성은 없습니다. 다만, final의 속성을 자동으로 갖게 된다는 사실만 기억하면 됩니다.
역시 이번에도 익명 객체 인스턴스를 사용하는 예제를 살펴보겠습니다.
public interface Calculate {
public int sum();
}
우선 인터페이스 Calculate를 선언합니다. 해당 인터페이스에는 sum()이라는 추상 메서드가 선언되어 있습니다.
public class Anonymous {
private int field;
public void method(final int arg1, int arg2) {
final int var1 = 0;
int var2 = 0;
field = 10;
// arg1 = 99;
// arg2 = 99;
// var1 = 99;
// var2 = 99;
Calculate calc = new Calculate() {
@Override
public int sum() {
int result = field + arg1 + arg2 + var1 + var2;
return result;
}
};
System.out.println(calc.sum());
}
}
/* 출력
10
*/
Anonymous 클래스에서 메서드의 로컬 변수로서 익명 객체를 활용합니다. 익명 객체 인스턴스 내에서 field, arg1, arg2, var1, var2가 사용되죠? 그래서 주석처리된 부분과 같이 해당 변수들은 초기값 이후의 값 수정이 불가능합니다.
'Programming > JAVA' 카테고리의 다른 글
제네릭(1) - 제네릭 타입 2 (0) | 2023.04.25 |
---|---|
제네릭(1) - 제네릭 타입 1 (0) | 2023.04.25 |
중첩 클래스&인터페이스(5) - 익명 객체 6 (0) | 2023.04.23 |
중첩 클래스&인터페이스(5) - 익명 객체 5 (0) | 2023.04.20 |
중첩 클래스&인터페이스(5) - 익명 객체 4 (0) | 2023.04.18 |