내부 클래스(inner class)란?
클래스 내에 선언된 클래스를 의미한다. 두 클래스가 밀접한 관계가 있는 경우 내부 클래스로 선언하며, 이 경우 클라이언트에게 불필요한(거의 사용하지 않는) 클래스를 숨겨 코드의 복잡성을 줄일 수 있다는 장점이 있다.
형태는 다음과 같다.
class ClassA { //외부 클래스
//...
class ClassB { //내부 클래스
//...
}
//...
}
내부 클래스는 외부 클래스의 멤버로 취급되며 선언된 위치에 따라 종류와 특징이 달라진다. 책의 예제를 통해 확인해보자.
class Outer {
// 인스턴스 클래스
// 주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용
class InstanceInner {}
// 스태틱 클래스
// 주로 외부 클래스의 static 멤버 또는 static 메서드에서 사용
static class StaticInner {}
void method() {
// 지역 클래스
// 외부 클래스의 메서드나 초기화 블럭 안에 선언, 선언된 영역 내부에서만 사용
class LocalInner {}
}
}
내부 클래스의 제어자와 접근성
내부 클래스는 외부 클래스의 멤버로 취급되기 때문에 멤버에 적용 가능한 규칙을 내부 클래스에도 적용할 수 있다. 즉, 내부 클래스 선언시 제어자를 적용할 수 있다. 뿐만아니라 내부 클래스도 클래스이기 때문에 클래스 내에서 제어자를 사용할 수 있다. 하지만 static 접근 제어자를 사용할 때는 주의가 필요하다. 주의점에 대해선 책의 예제 코드를 통해 알아보자.
class Outer {
private class InstanceInner {
static int value = 1; // 실패 : 해당 클래스는 인스턴스 생성 후에 사용 가능
static final int value = 1; // OK : 상수 취급
}
protected static class StaticInner {
static int value = 1;
static final int value = 1;
}
void method() {
class LocalInner {
static int value = 1; // 실패 : 해당 클래스는 인스턴스 생성 후에 사용 가능
static final int value = 1; // OK : 상수 취급
}
}
}
내부 클래스 사용 시 주의할 점이 있다. 우선 내부 클래스에서 외부 클래스의 지역 변수를 접근하는 것은 상수만 가능하다. 그 이유는 메서드가 수행을 마쳐서 지역 변수가 소멸된 시점에도 지역 클래스의 인스턴스가 소멸된 지역 변수를 참조하려는 경우가 발생할 수 있기 때문이다. 상수만 접근 가능한 이유는 JVM constant pool에서 상수를 따로 관리해서 일반적인 지역 변수와 생명주기가 다르기 때문이다.
외부 클래스와 내부 클래스의 변수 이름이 중복된 경우, 외부 클래스의 변수에 대한 접근은 '외부클래스명.this.변수명'의 형식으로 접근한다.
class Outer {
int number = 10; // Outer.this.number
class Inner {
int number = 20; // this.number
void method() {
int number = 30; // number
System.out.println(number);
System.out.println(this.number);
System.out.println(Outer.this.number);
}
}
void method() {
int value1 = 1;
final int value2 = 2; // java8부터 final 생략 가능, 컴파일 시 컴파일러가 자동으로 붙여줌
class LocalInner {
// 외부 클래스의 지역 변수는 상수만 접근 가능
int value3 = value1; // java8부터 에러 아님
int value4 = value2; // OK
}
}
}
익명 클래스(anonymous class)
클래스의 선언과 객체의 생성을 동시에 하는 클래스로 오직 하나의 객체만 생성할 수 있는 일회용 클래스이다. 이름이 없기 때문에 생성자를 가질 수 없으며, 오로지 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만을 구현할 수 있다. 형태는 다음과 같다.
new 상위 클래스 이름() {
//멤버
}
or
new 구현 인터페이스 이름() {
//멤버
}
책의 예제를 통해 간단한 익명 클래스 활용을 알아보자.
//예제.1
class InnerEx7 {
public static void main(String[] args) {
Button b = new Button("Start");
// 익명 클래스
b.addActionListener(new EventHandler());
}
}
class EventHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Occurred");
}
}
//예제.2
class InnerEx7 {
public static void main(String[] args) {
Button b = new Button("Start");
// 익명 클래스
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Occurred");
}
});
}
}
※ 익명 클래스의 클래스 파일명은 '외부 클래스명$숫자.class'의 형식이다.
Q&A
Q. 내부 클래스란?
A. 클래스 내에 선언된 클래스로 두 클래스가 밀접한 관계가 있을 때 이와같이 선언하며, 코드의 복잡성을 줄일 수 있다.
Q. 익명 클래스란?
A. 클래스의 선언과 객체의 생성을 동시에 하며 오직 하나의 객체만을 생성할 수 있는 일회용 클래스이다.
참고자료
- Java의 정석
- JVM contant pool
'개념서 > Java' 카테고리의 다른 글
| [Java] 예외처리 (0) | 2022.06.27 |
|---|---|
| [Java] 인터페이스 (0) | 2022.06.23 |
| [Java] 다형성 (0) | 2022.06.17 |
| [Java] 제어자 (0) | 2022.06.13 |
| [Java] package와 import (0) | 2022.06.10 |
댓글