내부 클래스는 클래스 내에 선언된다는 점을 제외하고 일반적인 클래스와 같다
사용빈도가 그렇게 높지 않으므로 기본 원리와 특징을 이해하는 정도까지만 학습해도 충분하다.
말 그대로 클래스 내에 선언된 클래스다.
클래스에 다른 클래스를 선언하는 이유는 두 클래스가 서로 긴밀한 관계에 있기 때문이다.
한 클래스를 다른 클래스의 내부 클래스로 선언하면
두 클래스의 멤버들 간에 서로 쉽게 접근할 수 있다는 장점과
외부에는 불필요한 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다는 장점이 있다.
내부 클래스의 장점
- 외부 클래스 멤버들을 내부 클래스가 쉽게 접근할 수 있다.
- 코드의 복잡성을 줄일 수 있다. (캡슐화)
아래 코드의 A와 B 두 개의 독립적인 클래스를
class A {
...
}
class B {
...
}
아래 코드와 같이 바꾸면
B는 A의 내부 클래스(inner class)가 되고
A는 B를 감싸고 있는 외부 클래스 (outer class)가 된다.
class A { // 외부 클래스
...
class B { //내부 클래스
...
}
...
}
! 이 때 내부 클래스인 B는 외부 클래스인 A를 제외하고 다른 클래스에서 잘 사용되지 않는 것이어야 한다.
내부 클래스의 종류는 변수의 선언 위치에 따른 종류와 같다.
내부 클래스는 마치 변수를 선언하는 것과 같은 위치에 선언할 수 있으며,
변수의 선언 위치에 따라 인스턴스 변수, 클래스 변수(static변수), 지역 변수로 구분되는 것과 같이
내부 클래스도 선언 위치에 따라 구분된다.
내부 클래스의 유효범위와 성질이 변수와 유사하므로 서로 비교하면서 보면 서로 도움이 될 것이다.
내부 클래스 : 인스턴스 클래스, 스태틱 클래스, 지역 클래스, 익명 클래스
내부 클래스: 인스턴스 클래스
외부 클래스의 멤버 변수 선언 위치에 선언하며, 외부 클래스의 인스턴스 멤버처럼 다뤄진다.
주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용될 목적으로 선언된다.
내부 클래스: 스태틱 클래스
외부 클래스의 멤버 변수 선언 위치에 선언하며, 외부 클래스의 static멤버처럼 다뤄진다.
주로 외부 클래스의 static멤버, 특히 static메서드에서 사용될 목적으로 선언된다.
내부 클래스: 지역 클래스
외부 클래스의 메서드나 초기화 블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다.
내부 클래스: 익명 클래스
클래스의 선언과 객체의 생성을 동시에 하는 이름 없는 클래스(일회용)
아래 코드에는 외부 클래스(Outer)에 3개의 서로 다른 종류의 내부 클래스를 선언했다.
class Outer{
int iv = 0;
static int cv = 0;
void myMethod() {
int lv = 0;
}
}
위 아래의 코드를 비교해 보면 내부 클래스의 선언 위치가 변수의 선언 위치와 동일함을 알 수 있다.
class Outer{
class InstanceI:nner {}
static class StaticInner {}
void myMethod() {
class LocalInner {}
}
}
변수가 선언된 위치에 따라 인스턴스 변수, 클래스 변수(static 변수), 지역변수로 나뉘듯이
내부 클래스도 이와 마찬가지로 선언된 위치에 따라 나뉜다.
그리고 내부 클래스의 선언 위치에 따라서 같은 선언 위치의 변수와 동일한 유효 범위(scope)와
접근성(accessibility)을 갖는다.
아래 코드에서 인스턴스 클래스(InstanceInner)와 스태틱 클래스(StaticInner)는
외부 클래스(Outer)의 멤버변수(인스턴스 변수와 클래스 변수)와 같은 위치에 선언되며,
또한 멤버 변수와 같은 성질을 갖는다.
따라서 내부 클래스가 외부 클래스의 멤버와 같이 간주되고,
인스턴스 멤버와 static멤버 간의 규칙이 내부 클래스에도 똑같이 적용된다
class Outer {
private int iv = 0;
protected static int cv = 0;
void myMethod() {
int lv = 0;
}
}
=>
class Outer {
private class :InstanceInner {}
protected static class StaticInner {}
void myMethod() {
class LocalInner {}
}
}
그리고 내부 클래스도 클래스이기 때문에 abstract나 final과 같은 제어자를 사용할 수 있을 뿐만 아니라,
멤버변수들처럼 private, protected과 접근 제어자도 사용이 가능하다.
내부 클래스 중에서 스태틱 클래스(StaticInner)만 static멤버를 가질 수 있다.
드문 경우지만 내부 클래스에 static변수를 선언해야한다면 스태틱 클래스로 선언해야 한다.
다만 final과 static이 동시에 붙은 변수는 상수(constant)이므로 모든 내부 클래스에서 정의가 가능하다.
인스턴스 멤버는 같은 클래스에 있는 인스턴스 멤버와 static멤버 모두 직접 호출이 가능하지만,
static멤버는 인스턴스 멤버를 직접 호출할 수 없는 것처럼,
인스턴스 클래스는 외부 클래스의 인스턴스 멤버를 객체 생성 없이 바로 사용할 수 있지만,
스태틱 클래스는 외부 클래스의 인스턴스 멤버를 객체 생성 없이 사용할 수 없다.
마찬가지로 인스턴스 클래스는 스태틱 클래스의 멤버들을 객체 생성 없이 사용할 수 있지만,
스태틱 클래스에서는 인스턴스 클래스의 멤버들을 객체 생성 없이 사용할 수 없다.
익명 클래스는 특이하게도 다른 내부 클래스들과는 다르게 이름이 없다.
클래스의 선언과 객체의 생성을 동시에 하기 때문에
단 한번만 사용될 수 있고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스다.
new 조상 클래스 이름() {
// 멤버 선언
}
or
new 구현 인터페이스 이름() {
// 멤버 선언
}
이름이 없기 때문에 생성자도 가질 수 없으며,
조상 클래스의 이름이나 구현하고자 하는 인터페이스의 이름을 사용해서 정의하기 때문에
하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스를 구현할 수 없다.
오로지 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만을 구현할 수 있다.
class InnerEx6 {
Object iv = new Object() { void method() {} }; // 익명 클래스
static Object cv = new Object() { void method() {} }; // 익명 클래스
void myMethod() {
Object lv = new Object() { void method() {} }; // 익명 클래스
}
}
위의 예제는 단순히 익명 클래스의 사용 예시를 보여준 것이다.
이 예제를 컴파일 하면 다음과 같이 4개의 클래스 파일이 생성된다.
InnerEx6.class
InnerEx6$1.class <-익명 클래스
InnerEx6$2.class <-익명 클래스
InnerEx6$3.class <-익명 클래스
익명 클래스는 이름이 없기 때문에 '외부 클래스명$숫자.class'의 형식으로 클래스 파일명이 결정된다.