자바에서 내부 클래스(Inner Class)는 한 클래스 내부에 선언된 클래스를 말합니다. 내부 클래스를 선언하게 되면 내부 클래스와 외부 클래스라고 표현할 수 있습니다. 내부 클래스는 보통 외부 클래스와 연관이 있는 경우가 많고 다른 곳에서 거의 사용할 일이 없는 경우에 내부 클래스를 선언해서 활용을 합니다.
package innerclass;
public class OuterClass1 {
private int num = 10;
// 클래스 파일안에 내부에 또 클래스 선언 (중첩클래스) // 인스턴스 내부 클래스
class InnerClass {
public void display() {
System.out.println("num : " + num);
}
}
} // end of outer class
package innerclass;
public class Main1 {
public static void main(String[] args) {
// 내부 클래스가 일반 멤버 클래스로 설계된 경우
// 외부 클래스 객체가 생성되고 내부 클래스를 생성할 수 있다.
OuterClass1 outerClass = new OuterClass1();
OuterClass1.InnerClass innerClass = outerClass.new InnerClass();
innerClass.display();
}
}
중첩 클래스는 “클래스 안에 들어있는 클래스”이고, 네 예제의 내부 클래스는 바깥 객체에 붙어서 생성되며 바깥 클래스의 멤버를 쉽게 사용할 수 있는 구조야.
즉 바깥 클래스가 생성이 되야 내부클래스를 생성할 수 있다.
package innerclass;
public class OuterClass2 {
private static int num = 10;
// 정적 내부 클래스
static class InnerClass {
public void display() {
System.out.println("num : " + num);
}
}
} // end of outer class
지금 정적 내부클래스가 num에 접근 할 수 있는 이유는 num도 정적 변수이기 때문이다.
package innerclass;
public class Main2 {
public static void main(String[] args) {
// 경우1
// Inner Class 일반 멤버 클래스로 설계된 경우
// 인스턴스 내부 클래스(기본)
// - 외부 클래스와 강하게 연결됨 상태
// - 외부 객체가 있어야 내부 객체를 호출할 수 있음.
// 경우 2
// static Inner Class 정적 클래스로 설계된 경우
// - 외부 클래스와 이름만 묶인 상태
// - 외부 객체 생성없이도 생성 가능
OuterClass2.InnerClass inner = new OuterClass2.InnerClass();
inner.display();
}
}
OuterClass1 outer = new OuterClass1();
OuterClass1.InnerClass inner = outer.new InnerClass();
일반 멤버 내부클래스에서는 이렇게 생성을 했었는데
OuterClass2.InnerClass inner = new OuterClass2.InnerClass();
정적 내부클래스는 이렇게 바로 생성할 수 있다.
package innerclass;
// 정적 내부 클래스 연습
public class Spaceship {
private Engine engine;
// 엔진을 추가한다
public void addEngine(Engine engine) {
this.engine = engine;
}
public void startSpaceShip() {
if(engine == null) {
System.out.println("엔진을 먼저 장착해주세요");
} else {
System.out.println("우주여행을 출발 합니다");
}
}
// 논리적으로 강하게 그룹화 시킬 때 내부 클래스를 사용할 수 있다.
// 정적 내부 클래스 engine
public static class Engine {
private static int engineCount = 0;
private int serialNumber;
public Engine() {
this.serialNumber = ++engineCount;
}
public void start() {
System.out.println("Engine " + serialNumber + " 동작 합니다");
}
} // end of inner static class
}
package innerclass;
public class SpaceshipMain {
public static void main(String[] args) {
// 정적 내부클래라서 바로 생성 가능함
Spaceship.Engine engine1 = new Spaceship.Engine();
Spaceship spaceship = new Spaceship();
spaceship.addEngine(engine1);
spaceship.startSpaceShip();
} // end of main
}
이렇게 정적 내부클래스를 쓸 수 있다.
package innerclass;
// 지역 내부 클래스(Local inner class)
public class OuterClass3 {
public void display() {
// 메서드 안에 클래스를 선언
class LocalInnerClass {
void printMessage() {
System.out.println("지역 내부 클래스의 메서드");
}
} // 지역 내부 클래스
// 지역 내부 클래스의 인스턴스 생성
LocalInnerClass innerClass = new LocalInnerClass();
innerClass.printMessage();
}
}
지역 내부 클래스는 특정 메서드 내에서만 정의되고 사용되는 클래스로, 메서드 실행 시에만 존재하고 메서드의 지역 변수처럼 동작합니다. 이 클래스는 외부에서는 접근할 수 없으며, 주로 메서드 내에서 일회성 작업을 수행하는 객체를 생성할 때 유용 합니다.
package innerclass;
public class Main3 {
public static void main(String[] args) {
OuterClass3 outerClass3 = new OuterClass3();
outerClass3.display();
} // end of main
}
이렇게 외부에서는 바로 접근은 못하게 display메서드를 실행해서 안에 localinnerclass가 정의가 되고 그다음 밑에서 객체를 생성하여 printmessage메서드를 호출하고 있는 모습이다.
지역 내부클래스는 내부에서 생성하고 실행되어야 하기에 이렇게 실행하는 모습이다.
display() 안에서 잠깐 정의된 지역 내부 클래스는
그 메서드 바깥으로 못 나와.
package innerclass;
// 익명 내부클래스 사용해 보기
public class OuterClass4 {
// 1.
Runnable runnable; // 인터페이스
// 1.
public OuterClass4() {
// 인터페이스를 구현 익명 클래스로 생성시킬 수 있다.
// 내부에서 추상 메서드를 일반 메서드로 재 정의해서 마치 실제 클래스 처럼 구현해서 사용할 수 있다.
new Runnable() {
@Override
public void run() {
//...
}
};
} // end of 생성자
// 2.
// 멤버 변수 선언과 동시에 인터페이스를 구현 클래스로 동시에 초기화
Runnable runnable2 = new Runnable() {
@Override
public void run() {
}
};
}
원래는 클래스를 만들고 상속하여 객체를 생성했는데 익명클래스는
변수 선언과 동시에 인터페이스를 구현 클래스로 동시에 총기화하여 만들었다.
class 이름없는클래스 implements Runnable {
@Override
public void run() {
System.out.println("실행");
}
}
Runnable runnable2 = new 이름없는클래스();
내부적으로는 이렇게 이루어 지고 있는 것이다.