내부 클래스(inner class)
내부 클래스는 AWT나 Swing같은 GUI Application 이벤트 처리를 제외하고는 잘 쓰이지 않는다.
이런 게 이렇게 작동하는구나 정도만 이해하고 넘어가자
내부 클래스란?
내부 클래스: 클래스 내에 선언된 클래스
클래스에 다른 클래스 선언하는 이유 : 두 클래스가 서로 긴밀한 관계에 있기 때문
- 내부 클래스로 선언하면 두 클래스의 멤버들 간에 서로 쉽게 접근할 수 있다는 장점이 있고, 외부에는 불필요한 클래스를 감춰 코드 복잡성을 줄일 수 있다.
class A {
//
class B {
//
}
}
class A는 외부 클래스, class B는 내부 클래스이다.
- 이 때 내부 클래스인 B는 외부 클래스인 A를 제외하고는 다른 클래스에서 잘 사용되지 않는 것이어야 한다.
내부 클래스의 종류와 특징
내부 클래스의 종류는 변수의 선언위치에 따른 종류와 같다.
인스턴스 클래스 : 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스 멤버처럼 다루어진다.
스태틱 클래스 : 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static 인스턴스 멤버처럼 다루어진다.
지역 클래스 : 외부 클래스의 메서드나 초기화 블럭 안에서 선언하며, 선언된 영역 내부에서만 사용될 수 있다.
익명 클래스 : 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용)
내부 클래스의 제어자와 접근성
내부 클래스의 인스턴스 클래스와 스태틱 클래스는 멤버변수와 같은 성질을 갖는다
- 즉, 내부 클래스가 외부 클래스의 멤버와 같이 간주된다
내부 클래스도 클래스이기 때문에 abstract나 final과 같은 제어자를 사용할 수 있고, 멤버 변수처럼 private나 protected과 같은 접근 제어자도 사용이 가능하다
Example 7.31
class InnerEx1 {
class InstanceInner {
int iv = 100;
// static int cv = 100; // 컴파일 에러. staic변수를 선언할 수 없음
final static int CONST = 100; // final static은 상수이므로 허용
}
static class StaticInner {
int iv = 100;
static int cv = 200; // static클래스만 static멤버를 정의할 수 있음
}
void myMethod() {
class LocalInner {
int iv = 300;
// static int cv = 300; // 에러. static변수를 선언할 수 없음
final static int CONST = 300; // final static은 상수이므로 허용
}
}
public static void main(String args[]) {
System.out.println(InstanceInner.CONST);
System.out.println(StaticInner.cv);
}
}
내부 클래스 중에서 스태틱 클래스만 static 멤버를 가질 수 있다.
- 드물지만 내부 클래스에 static 변수를 선언해야 한다면 스태틱 클래스로 선언해야 한다
다만 final과 static이 동시에 붙은 변수는 상수이므로 모든 내부 클래스에서 정의가 가능하다
Example 7.33
class InnerEx3 {
private int outerIv = 0;
static int outerCv = 0;
class InstanceInner {
int iiv = outerIv; // 외부클래스의 private멤버도 접근 가능
int iiv2 = outerCv;
}
static class StaticInner {
// static클래스는 외부클래스의 인스턴스멤버에 접근할 수 없음
// int siv = outerIv;
static int scv = outerCv;
}
void myMethod() {
int lv = 0;
final int LV = 0; // JDK1.8부터 final 생략 가능
class LocalInner {
int liv = outerIv; // myMethod()가 static인 경우 컴파일 에러.
int liv2 = outerCv;
// 외부클래스의 지역변수는 final이 붙은 변수(상수)만 접근 가능
int liv3 = lv; // 컴파일 에러. (JDK1.8부터 에러 아님)
int liv4 = LV; // O
}
}
}
인스턴스 클래스는 외부 클래스의 인스턴스 멤버이기 때문에 인스턴스 변수 outerIv와 static변수 outerCvf를 모두 사용할 수 있다.
- 심지어 접근제어자가 private라도 가능하다
스태틱 클래스는 단지 static 멤버인 outerCv만 사용 가능하다
지역 클래스는 외부 클래스의 인스턴스멤버와 static멤버를 모두 사용할 수 있으며, 지역 클래스가 포함된 메서드에 정의된 지역 변수도 사용할 수 있다.
- 단, final이 붙은 지역변수만 접근가능한데, 메서드가 수행을 마쳐서 지역변수가 소멸된 시점에도, 지역 클래스의 인스턴스가 소멸된 지역변수를 참조하려는 경우가 발생할 수 있기 때문이다.
- 주의) JDK 1.8부터 지역 클래스에서 접근하는 지역 변수 앞에 final을 생략할 수 있게 바뀌었다. 이걸 모르고 해당 변수 값을 바꾸면 컴파일 에러가 발생한다
Example 7.34
class Outer {
class InstanceInner {
int iv = 100;
}
static class StaticInner {
int iv = 200;
static int cv = 300;
}
void myMethod() {
class LocalInner {
int iv = 400;
}
}
}
class InnerEx4 {
public static void main(String[] args) {
// 인스턴스클래스의 인스턴스를 생성하려면
// 외부클래스의 인스턴스를 먼저 생성해야 한다.
Outer oc = new Outer();
Outer.InstanceInner ii = oc.new InstanceInner();
System.out.println("ii.iv : " + ii.iv);
System.out.println("Outer.StaticInner.cv : " + Outer.StaticInner.cv);
// static내부클래스의 인스턴스는 외부클래스를 먼저 생성하지 않아도 된다.
Outer.StaticInner si = new Outer.StaticInner();
System.out.println("si.iv : " + si.iv);
}
}
- 외부 클래스가 아닌 다른 클래스에서 내부 클래스를 생성하고 내부 클래스의 멤버에 접근하는 예제인데, 이런 경우가 발생하게 코드를 작성하면 안 됩니다!
Example 7.35
class Outer {
int value = 10; // Outer.this.value
class Inner {
int value = 20; // this.value
void method1() {
int value = 30;
System.out.println(" value : " + value);
System.out.println(" this.value : " + this.value);
System.out.println("Outer.this.value : " + Outer.this.value);
}
} // Inner클래스의 끝
} // Outer클래스의 끝
class InnerEx5 {
public static void main(String args[]) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.method1();
}
} // InnerEx5 끝
- 내부 클래스와 외부 클래스에 선언된 변수의 이름이 같을 때 변수 앞에 'this'또는 '외부 클래스명.this'를 붙여서 서로 구별할 수 있다.
익명 클래스(anonymous class)
다른 내부 클래스들과 다르게 이름이 없다.
클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한번만 사용될 수 있고, 오직 하나의 객체만을 생성할 수 있는 일회용 클래스이다.
new 조상클래스이름() {
// 멤버 선언
}
or
new 구현인터페이스이름() {
// 멤버 선언
}
이름이 없기 때문에 생성자도 가질 수 없다.
조상클래스의 이름이나 구현하고자 하는 인터페이스의 이름을 사용해서 정의하기 때문에 하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나, 둘 이상의 인터페이스를 구현할 수 없다.
- 오직 단 하나의 클래스를 상속받거나, 단 하나의 인터페이스만을 구현할 수 있다.
내부 클래스 -> 익명 클래스
내부 클래스
import java.awt.*;
import java.awt.event.*;
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("ActionEvent occurred!!!");
}
}
익명 클래스
import java.awt.*;
import java.awt.event.*;
class InnerEx8 {
public static void main(String[] args) {
Button b = new Button("Start");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("ActionEvent occured!!!");
}
} // 익명클래스의 끝
);
} // main의 끝
}
참조) Java의 정석 3rd edition(남궁성, 도우출판)
'Programming Language > Java' 카테고리의 다른 글
[Java] 13. 예외 처리(Exception handling)(2) - try-catch 문 (0) | 2024.07.09 |
---|---|
[Java] 12. 예외 처리(Exception handling)(1) - 예외 처리란? (0) | 2024.07.09 |
[Java] 10. OOP - 인터페이스(Interface) (0) | 2024.07.07 |
[Java] 09. OOP - 추상 클래스(abstract class) (0) | 2024.06.24 |
[Java] 08. OOP - 다형성(polymorphism) (0) | 2024.06.24 |