익명 내부 클래스란? 이름이 없는 클래스의 일종이다!
클래스의 이름을 생략하고 주로 하나의 인터페이스나 하나의 추상 클래스를 구현하여 반환하는 형식이다!
위 코드는 View 클래스의 내부 인터페이스인 OnClickListener 를 구현한, 익명 클래스를 반환하는 것을 보여준다!
추가로, 반환된 익명 클래스 내부의 onClick 메서드를 override 한 것이다.
class Outer{
int outNum = 100;
static int sNum = 200;
Runnable getRunnable(int i){
int num = 100;
class MyRunnable implements Runnable{
int localNum = 10;
@Override
public void run() {
...
}
}
return new MyRunnable();
}
}
위 코드를 보면 getRunnable 메서드를 통해 Runnable을 구현한 MyRunnable을 반환한다.
그런데 사실 MyRunnable 클래스의 이름은 반환 이 외에 사용되지 않는다.
따라서 이를 익명으로 바꿀 수 있다.
class Outer2{
int outNum = 100;
static int sNum = 200;
Runnable getRunnable(int i){ // 익명 내부 클래스
int num = 100;
return new Runnable(){
@Override
public void run() {
...
};
}
}
MyRunnable 클래스의 이름을 익명으로 하여 반환할 수 있음을 확인했다!
추가적으로 java8에서 부터 도입된 람다식 역시 익명 내부 클래스로 구현되어진다.
자세한 코드 설명은 람다식 관련 글에서 확인할 수 있다.
그렇다면 이제는 해당 Outer2의 클래스 내부에 선언 된 변수의 허용범위에 대해 알아보겠다
class Outer2 {
int outNum = 100;
static int sNum = 200;
Runnable getRunnable(int i){ // 익명 내부 클래스
int num = 100;
return new Runnable(){
int localNum = 10;
@Override
public void run() {
System.out.println("i =" + i);
System.out.println("num = " +num);
System.out.println("localNum = " +localNum);
System.out.println("outNum = " + outNum + "(외부 클래스 인스턴스 변수)");
System.out.println("Outter.sNum = " + Outer.sNum + "(외부 클래스 정적 변수)");
}
};
}
}
public class AnonymousInnerTest {
public static void main(String[] args) {
Outer2 out2 = new Outer2();
Runnable runner2 = out.getRunnable(10);
runner2.run();
}
}
자! 코드를 한번 쓰윽~ 훑어본 후 한번 생각해보자!
@Override
public void run() {
System.out.println("i =" + i);
System.out.println("num = " +num);
System.out.println("localNum = " +localNum);
System.out.println("outNum = " + outNum + "(외부 클래스 인스턴스 변수)");
System.out.println("Outter.sNum = " + Outer.sNum + "(외부 클래스 정적 변수)");
}
그럼, 위 출력 부분의 getRunnable의 파라미터 i와, getRunnable의 지역변수 num을 출력하는 부분은 오류가 나야되는 게 정상이지 않을까??
하지만 위 코드에서 결과는 아래와 같이 정상적으로 나온다....!
어찌된 일인고.... 알아보니!
메서드 종료 이후에도, 메서드 호출당시 받은 매개변수나 지역변수가 반환된 내부 클래스에서 사용해야 하는 경우가 있을 수 있으므로 지역 내부 클래스에서 사용하는 메서드의 지역 변수나 매개 변수는 final로 선언된다!!
호오,,,, 그렇다는 말은 즉
지역 내부 클래스에서 사용하는 메서드의 지역 변수나 매개 변수가 Stack영역이 아닌 상수영역 즉 Code영역에 저장이 된다.
따라서!
@Override
public void run() {
//num = 200; //에러 남. 지역변수는 상수로 바뀜
//i = 100; //에러 남. 매개 변수 역시 지역변수처럼 상수로 바뀜
System.out.println("i =" + i);
System.out.println("num = " +num);
System.out.println("localNum = " +localNum);
System.out.println("outNum = " + outNum + "(외부 클래스 인스턴스 변수)");
System.out.println("Outter.sNum = " + Outer.sNum + "(외부 클래스 정적 변수)");
}
}
위 주석 처리한 부분을 주석 제거 하면, 에러가 난다.
이 글을 처음 부터 읽었다면 그 이유는 이제 자신있게 말할 수 있을 거라 생각한다 ㅎㅎ
정답은! Final로 선언되기 때문이다.
위와 같은 상황은 실제로 에러가 나면 왜 나는 지 잘 모를 수 있을 것 같다는 생각이들었다...
이번 글을 계기로 언어를 사용함에 있어 항상 기본에 충실하고, 개념적 이해가 필수 임을 알 수 있었다 !!
공부하면 할 수록 더 많이 보이는 것 같아 조금은 기쁘다 ㅎ
한번에 끝내는 Java/Spring 웹 개발 마스터 초격차 패키지 Online. Part2/ch6-1