내부 클래스는 클래스 내에 선언된 클래스이다. 클래스 안에 클래스를 선언하는 이유는 두 클래스가 서로 긴밀한 관계에 있기 때문이다.
내부 클래스의 장점
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
- 코드의 복잡성을 줄일 수 있다.(캡슐화)
(1)
class A {
...
}
class B {
...
}
(2)
class A{
...
class B{
...
}
}
B는 내부 클래스(inner class) A는 외부 클래스(outer class)가 된다. 이 때, 내부 클래스인 B는 외부 클래스인 A를 제외하고는 다른 클래스에서 잘 사용되지 않는 것이어야 한다.
내부 클래스의 종류는 변수의 선언 위치에 따른 종류와 같다. 각 내부 클래스의 선언 위치에 따라 같은 선언 위치의 변수와 동일한 유효범위와 접근성을 갖는다.
class Outer{
class InstanceInner{
int iv = 100;
static int cv = 150; // 에러! static 클래스가 아니므로, static 변수 선언 불가.
final static int CONST = 180; // final static은 상수임으로 허용.
}
static class staticInner{
int iv = 200;
static int cv = 250;
}
}
public class InnerEx4 {
public static void main(String[] args) {
// 스태틱 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.
System.out.println("Outer.StaticInner.cv : "+Outer.staticInner.cv);
System.out.println("ii.CONST : "+Outer.InstanceInner.CONST);
Outer.staticInner si = new Outer.staticInner();
System.out.println("si.iv : "+si.iv);
// 인스턴스 클래스의 인스턴스 생성하려면 외부 클래스의 인스턴스 먼저 생성해야함.
Outer oc = new Outer();
Outer.InstanceInner ii = oc.new InstanceInner();
System.out.println("ii.iv : "+ii.iv);
}
}
내부 클래스 중에서 스태틱 클래스만 static 멤버를 가질 수 있다. 다만 final static이 동시에 붙은 변수는 상수(constant)이므로 모든 내부 클래스에서 정의가 가능하다.
인스턴스 멤버는 같은 클래스에 있는 인스턴스 멤버와 static 멤버 모두 직접 호출이 가능하지만, static 멤버는 인스턴스 멤버를 직접 호출할 수 없는 것처럼, 인스턴스 클래스는 외부 클래스의 인스턴스 멤버를 객체 생성 없이 바로 사용할 수 있지만, 스태틱 클래스는 외부 클래스이 인스턴스 멤버를 객체 생성 없이 사용할 수 없다.
class Outer{
int value = 10;
class Inner {
int value = 20;
void method() {
int value = 30;
System.out.println("value = "+value);
System.out.println("this.value = "+this.value);
System.out.println("Outer.this.value = "+Outer.this.value);
}
}
}
public class InnerEx5 {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.method();
}
}
내부 클래스와 외부 클래스에 선언된 변수의 이름이 같을 때 변수 앞에 'this' 또는 '외부 클래스명.this'를 붙여서 서로 구별할 수 있다.
익명 클래스는 다른 내부 클래스들과 달리 이름이 없다.
클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한번만 사용될 수 있고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스다.
new 부모 클래스명() {
...
}
or
new 구현 인터페이스명() {
...
}
이름이 없기 때문에 생성자도 가질 수 없으며, 부모 클래스나 구현하고자 하는 인터페이스의 이름을 사용해서 정의하기 때문에 하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스를 구현할 수 없다.
익명 클래스는 이름이 없기 때문에 '외부 클래스명$숫자.class'의 형식으로 클래스 파일명이 결정된다.
(1)
class Inner{
public static void main(String [] args){
Button b = new Button("start");
b.addActionListener(new EventHandler());
}
}
class EventHandler implements ActionLisener{
public void actionPerformed(ActionEvent e){
System.out.println("ActionEvent occurred!");
}
}
(2)
class Inner{
public static void main(String [] args){
Button b = new Button("start");
b.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
System.out.println("ActionEvent occurred!!!");
}
}
}
}
(1)번 코드를 익명클래스를 이용해 변경한 것이 (2)다.