Java 중첩/내부 클래스 마스터: 종류, 예제, 모범 사례
Java 중첩 클래스와 내부 클래스
Java 중첩 클래스가 캡슐화를 강화하고 가독성을 개선하며 강력한 내부 클래스 패턴을 가능하게 하는 방법을 알아봅니다.
Java에서 클래스 안에 다른 클래스를 선언할 수 있습니다. 이를 중첩 클래스라고 합니다. 예:
class OuterClass { // ... class NestedClass { // ... } }
중첩 클래스에는 두 가지 주요 종류가 있습니다:
- Non-static 중첩 클래스 (흔히 내부 클래스라고 함)
- Static 중첩 클래스
추천 읽기:
- Java 접근 제어자
- Java Static 키워드
Non-static 중첩 클래스 (내부 클래스)
내부 클래스는 외부 클래스 내에 위치하며 외부 클래스 멤버에 자유롭게 접근 가능합니다(private 포함). 외부 클래스 인스턴스에 종속되므로 외부 클래스를 먼저 인스턴스화해야 합니다.
예제 1: 내부 클래스
class CPU { double price; // 내부 클래스 class Processor{ double cores; String manufacturer; double getCache(){ return 4.3; } } // protected 내부 클래스 protected class RAM{ double memory; String manufacturer; double getClockSpeed(){ return 5.5; } } } public class Main { public static void main(String[] args) { CPU cpu = new CPU(); CPU.Processor processor = cpu.new Processor(); CPU.RAM ram = cpu.new RAM(); System.out.println("Processor Cache = " + processor.getCache()); System.out.println("Ram Clock speed = " + ram.getClockSpeed()); } }
출력:
Processor Cache = 4.3 Ram Clock speed = 5.5
여기서 Processor와 RAM은 CPU 내에 중첩됩니다. RAM 클래스는 protected로 선언되어 내부 클래스가 Java 접근 제어자를 사용할 수 있음을 보여줍니다.
Main에서 외부 클래스 CPU를 먼저 인스턴스화한 후 외부 인스턴스를 통해 내부 클래스 인스턴스 생성:
CPU.Processor processor = cpu.new Processor; CPU.RAM ram = cpu.new RAM();
팁: 점(.) 연산자로 내부 클래스 인스턴스를 외부 인스턴스에 바인딩합니다.
내부 클래스에서 외부 클래스 멤버 접근
내부 클래스는 외부 클래스 이름을 this 키워드에 접두사로 붙여 외부 클래스 멤버를 참조할 수 있습니다(예: Car.this.carType). 내부 클래스 고유의 this 컨텍스트가 있을 때 이 문법이 필요합니다.
예제 2: 외부 클래스와 상호작용
class Car { String carName; String carType; public Car(String name, String type) { this.carName = name; this.carType = type; } private String getCarName() { return this.carName; } // 내부 클래스 class Engine { String engineType; void setEngine() { if(Car.this.carType.equals("4WD")){ if(Car.this.getCarName().equals("Crysler")) { this.engineType = "Smaller"; } else { this.engineType = "Bigger"; } } else { this.engineType = "Bigger"; } } String getEngineType(){ return this.engineType; } } } public class Main { public static void main(String[] args) { Car car1 = new Car("Mazda", "8WD"); Car.Engine engine = car1.new Engine(); engine.setEngine(); System.out.println("Engine Type for 8WD= " + engine.getEngineType()); Car car2 = new Car("Crysler", "4WD"); Car.Engine c2engine = car2.new Engine(); c2engine.setEngine(); System.out.println("Engine Type for 4WD = " + c2engine.getEngineType()); } }
출력:
Engine Type for 8WD= Bigger Engine Type for 4WD = Smaller
getCarName()이 private이지만 같은 외부 클래스 내에 있으므로 내부 클래스에서 호출 가능합니다.
Static 중첩 클래스
Static 중첩 클래스는 외부 클래스 인스턴스 없이 생성 가능합니다. 따라서 외부 클래스의 non-static 멤버에 직접 접근 불가. 외부 클래스 이름으로 생성:
OuterClass.NestedClass obj = new OuterClass.NestedClass();
예제 3: Static 중첩 클래스
class MotherBoard { static class USB{ int usb2 = 2; int usb3 = 1; int getTotalPorts(){ return usb2 + usb3; } } } public class Main { public static void main(String[] args) { MotherBoard.USB usb = new MotherBoard.USB(); System.out.println("Total Ports = " + usb.getTotalPorts()); } }
출력:
Total Ports = 3
예제 4: Static 중첩 클래스에서 외부 멤버 접근 시도
class MotherBoard { String model; public MotherBoard(String model) { this.model = model; } static class USB{ int usb2 = 2; int usb3 = 1; int getTotalPorts(){ if(MotherBoard.this.model.equals("MSI")) { return 4; } else { return usb2 + usb3; } } } } public class Main { public static void main(String[] args) { MotherBoard.USB usb = new MotherBoard.USB(); System.out.println("Total Ports = " + usb.getTotalPorts()); } }
실행 결과:
error: non-static variable this cannot be referenced from a static context
이 에러는 static 중첩 클래스가 외부 인스턴스 없이 MotherBoard.this를 참조할 수 없기 때문입니다.
핵심 요약
- 내부 클래스는 외부 클래스의 일반 멤버로 취급되어
private, protected, public 선언 가능 - 외부 클래스 모든 멤버에 자유롭게 접근 가능,
private 포함 - Static 중첩 클래스는 외부 인스턴스 불필요, non-static 외부 멤버 직접 접근 불가
- 중첩 클래스 사용으로 캡슐화 촉진, 관련 로직 묶음
- 내부 클래스는 외부 클래스와 긴밀히 결합된 콜백, 리스너, 헬퍼 객체에 이상적