Stay Hungry Stay Foolish

자바/자바 중급

[자바 중급] 08. 중첩 클래스, 내부 클래스 2

dev스카이 2024. 10. 11. 21:04

지역 클래스(Local class)

지역 클래스는 내부 클래스의 종류 중 하나로 내부 클래스의 특징을 그대로 가진다. 따라서 지역 클래스도 바깥 클래스의 인스턴스 멤버에 접근할 수 있다.

class Outer {
     public void process() {
         //지역 변수
         int localVar = 0;
         
         //지역 클래스
         class Local {...}
         
         Local local = new Local();
     }
}

 

 

특징

  • 지역 클래스는 지역 변수처럼 코드 블럭 안에 클래스를 선언한다.
  • 지역 클래스는 지역 변수에 접근할 수 있다.
  • 자신의 인스턴스 변수에 접근할 수 있다.
  • 자신이 속한 코드 블럭의 지역 변수 혹은 매개변수에 접근할 수 있다. (매개변수도 지역 변수의 한 종류이다)
  • 바깥 클래스의 인스턴스 멤버에 접근할 수 있다. 
  • 지역 클래스는 지역 변수처럼 접근 제어자를 사용할 수 없다.

 

📌 참고

내부 클래스를 포함한 중첩 클래스들도 일반 클래스처럼 인터페이스를 구현하거나, 부모 클래스를 상속할 수 있다.

 

 

지역 변수 캡처

지역 변수 캡처 개념이 왜 나왔는가? 지역 클래스는 지역 변수에 접근할 수 있다. 그리고 지역 변수의 생명 주기는 짧지만, 지역 클래스를 통해 생성한 인스턴스의 생명 주기는 길다. 지역 클래스를 통해 생성한 인스턴스가 지역 변수에 접근해야 하는데, 둘의 생명 주기가 다르기 때문에 인스턴스는 살아있지만, 지역 변수는 이미 제거된 상태일 수 있다. 이러한 문제를 해결하기 위해 나온 것이 지역 변수 캡처이다.

 

지역 변수 캡처란 지역 클래스의 인스턴스를 생성하는 시점에 필요한 지역 변수를 복사해서 생성한 인스턴스에 함께 넣어둔다. 즉 인스턴스를 생성할 때 필요한 지역 변수를 복사해서 보관해 둔다는 말이다. 모든 지역 변수를 캡처하는 것이 아니라 접근이 필요한 지역 변수만 캡처한다.

 

 

📌 주의 - 캡처 변수의 값이 변하면 안 된다. '사실상 final'이다.

  • 지역 변수의 값을 변경하면 인스턴스에 캡처한 변수의 값도 변경해야 한다.
  • 반대로 인스턴스에 있는 캡처 변수의 값을 변경하면 해당 지역 변수의 값도 다시 변경해야 한다.
  • 개발자 입장에서 보면 예상하지 못한 곳에서 값이 변경될 수 있다. 이는 디버깅을 어렵게 한다.
  • 지역 변수의 값과 인스턴스에 있는 캡처 변수의 값을 서로 동기화 해야 하는데, 멀티쓰레드 상황에서 이런 동기화는 매우 어렵고, 성능에 나쁜 영향을 줄 수 있다. 이 부분은 멀티쓰레드를 학습하면 이해할 수 있다.

위의 문제들 때문에 캡처 변수의 값을 변경하면 안 된다. 즉 지역 클래스가 접근하는 지역 변수는 final로 고정하거나 값을 변경하지 않아야 한다.

 

 

익명 클래스(nonymous class)

지역 클래스의 한 종류로, 클래스의 이름이 없다는 특징이 있다. 익명 클래스를 사용하면 아래와 같이 클래스의 이름을 생략하고, 클래스의 선언과 생성을 한번에 처리할 수 있다. 

Printer printer = new Printer(){
 	//body
};

 

 

특징

  • 익명 클래스는 이름 없는 지역 클래스를 선언하면서 동시에 생성한다.
  • 익명 클래스는 부모 클래스를 상속 받거나, 또는 인터페이스를 구현해야 한다. 익명 클래스를 사용할 때는 상위 클래스나 인터페이스가 필요하다.
  • 익명 클래스는 말 그대로 이름이 없다. 이름을 가지지 않으므로, 생성자를 가질 수 없다. (기본 생성자만 사용됨)
  • 익명 클래스는 AnonymousOuter$1 과 같이 자바 내부에서 바깥 클래스 이름 + $ + 숫자로 정의된다. 익명 클래스가 여러개면 $1 , $2 , $3 으로 숫자가 증가하면서 구분된다.

 

※ 익명 클래스를 사용하면 클래스를 별도로 정의하지 않고도 인터페이스나 추상 클래스를 즉석에서 구현할 수 있어 코드가 더 간결해진다. 하지만, 복잡하거나 재사용이 필요한 경우에는 별도의 클래스를 정의하는 것이 좋다.