내부 클래스(Inner Class)의 개념
내부 클래스는 기존 클래스의 내부에 중첩되어 정의된 클래스를 의미합니다. 외부 클래스의 멤버를 캡슐화하고, 논리적으로 연관된 클래스들을 그룹화하여 코드의 응집도를 높이는 데 주로 사용됩니다. 또한, 외부 클래스의 프라이빗(private) 멤버에 직접 접근할 수 있어 복잡한 구조를 단순화할 수 있습니다. 컴파일 시에는 외부 클래스와 별도의 독립적인 바이트코드(.class) 파일로 생성되며, 외부 클래스에 필요한 기능적 컴포넌트를 제공하는 역할을 수행합니다.
1. 멤버 내부 클래스 (Member Inner Class)
멤버 내부 클래스는 외부 클래스의 인스턴스 변수나 메서드와 동일한 수준에 정의됩니다. 외부 클래스의 모든 멤버에 접근할 수 있으며, 내부 클래스 자체는 정적(static) 멤버를 가질 수 없습니다(단, static final 상수는 예외). 인스턴스화를 위해서는 반드시 외부 클래스의 객체가 선행되어야 합니다.
package com.example.nested;
public class Corporation {
private String corpName = "TechGlobal";
private int employeeCount = 5000;
// 멤버 내부 클래스: 외부 클래스의 인스턴스 멤버와 동일한 수준에 위치
public class Division {
private String divName = "R&D";
private String corpName = "Innovation Lab"; // 외부 클래스와 동일한 이름의 변수
// 정적 멤버는 선언할 수 없으나, 상수(static final)는 허용됨
private static final int BUDGET = 1000000;
public void printDetails() {
System.out.println("부서명: " + this.divName);
System.out.println("내부 부서 코드: " + this.corpName);
// 외부 클래스의 동명 변수에 접근할 때는 '외부클래스명.this' 사용
System.out.println("외부 기업명: " + Corporation.this.corpName);
System.out.println("전체 직원 수: " + Corporation.this.employeeCount);
}
}
}
package com.example.nested;
public class CorporationTest {
public static void main(String[] args) {
// 1. 외부 클래스 객체 생성 후 내부 클래스 객체 생성
Corporation corp = new Corporation();
Corporation.Division div = corp.new Division();
div.printDetails();
// 2. 인스턴스화를 한 번에 수행하는 방식
Corporation.Division div2 = new Corporation().new Division();
}
}
2. 정적 내부 클래스 (Static Nested Class)
static 키워드로 선언된 내부 클래스입니다. 외부 클래스의 인스턴스에 종속되지 않으므로, 외부 클래스의 객체 생성 없이 직접 인스턴스화할 수 있습니다. 정적 멤버를 자유롭게 선언할 수 있지만, 외부 클래스의 인스턴스 변수에는 직접 접근할 수 없습니다.
package com.example.nested;
public class Database {
private String host = "localhost";
private int port = 3306;
// 정적 내부 클래스: 외부 클래스의 인스턴스와 독립적
public static class ConnectionPool {
private int maxSize = 50;
private static int activeConnections = 0;
public void displayConfig() {
// 정적 내부 클래스에서는 외부 클래스의 인스턴스 변수에 직접 접근 불가
Database db = new Database();
System.out.println("호스트: " + db.host);
System.out.println("포트: " + db.port);
System.out.println("최대 연결 수: " + this.maxSize);
System.out.println("활성 연결 수: " + ConnectionPool.activeConnections);
}
}
}
package com.example.nested;
public class DatabaseTest {
public static void main(String[] args) {
// 외부 클래스의 인스턴스 생성 없이 직접 객체화 가능
Database.ConnectionPool pool = new Database.ConnectionPool();
pool.displayConfig();
}
}
3. 지역 내부 클래스 (Local Inner Class)
외부 클래스의 메서드 블록 내부에 정의되는 클래스입니다. 유효 범위가 해당 메서드 내부로 국한되며, 접근 제한자(public, private 등)를 사용할 수 없습니다. 메서드의 지역 변수에 접근할 경우, 해당 변수는 사실상 최종(effectively final) 상태여야 생명 주기 문제를 방지할 수 있습니다.
package com.example.nested;
public class NetworkService {
private String protocol = "HTTPS";
public void establishConnection() {
// 지역 내부 클래스에서 접근하는 지역 변수는 사실상 최종(effectively final) 상태여야 함
final String endpoint = "api.example.com";
// 메서드 블록 내부에 정의된 지역 내부 클래스 (접근 제한자 사용 불가)
class Session {
private String sessionId = "ABC-123";
// 지역 내부 클래스 역시 정적 멤버를 가질 수 없으나 상수는 허용됨
private static final int MAX_RETRY = 3;
public void start() {
System.out.println("프로토콜: " + NetworkService.this.protocol);
System.out.println("엔드포인트: " + endpoint);
System.out.println("세션 ID: " + this.sessionId);
}
}
// 메서드 내부에서 객체 생성 및 사용
Session session = new Session();
session.start();
}
}
package com.example.nested;
public class NetworkTest {
public static void main(String[] args) {
NetworkService service = new NetworkService();
service.establishConnection();
}
}
4. 익명 내부 클래스 (Anonymous Inner Class)
클래스의 이름이 없는 지역 내부 클래스의 특수한 형태입니다. 반드시 하나의 부모 클래스를 상속하거나 인터페이스를 구현해야 합니다. 코드의 양을 줄여주고 즉각적인 구현이 가능하다는 장점이 있지만, 재사용이 불가능하고 복잡한 로직이 포함될 경우 가독성이 떨어진다는 단점이 있습니다.
package com.example.nested;
public interface Notifier {
void send(String message);
}
package com.example.nested;
public class NotificationTest {
public static void main(String[] args) {
// 일반적인 지역 내부 클래스를 활용한 구현
class EmailNotifier implements Notifier {
@Override
public void send(String message) {
System.out.println("이메일 전송: " + message);
}
}
Notifier email = new EmailNotifier();
email.send("안녕하세요");
// 익명 내부 클래스를 활용한 코드 최적화 (단일 사용 목적)
Notifier sms = new Notifier() {
@Override
public void send(String message) {
System.out.println("SMS 전송: " + message);
}
};
sms.send("반갑습니다");
}
}