내부 클래스란?
자바에서 클래스 안에 정의된 또 다른 클래스를 **내부 클래스(nested class)**라고 하며, 캡슐화, 유지보수, 가독성 향상 등의 목적에서 사용된다.
내부 클래스의 분류
내부 클래스는 크게 두 가지로 나뉜다.
내부 클래스
├── 1. 정적(static) 내부 클래스
└── 2. 비정적(non-static) 내부 클래스
├── 2-1. 인스턴스 내부 클래스 (member inner class)
├── 2-2. 지역 내부 클래스 (local inner class)
└── 2-3. 익명 내부 클래스 (anonymous inner class)
1️⃣ static nested class (정적 내부 클래스)
🔹 특징
- static으로 선언된 내부 클래스
- 외부 클래스의 정적(static) 멤버만 접근 가능
- 외부 클래스 인스턴스 없이도 사용 가능
- 자바 컴파일러는 .class 파일을 Outer$StaticNested.class 형식으로 생성함
🔹 주 용도
- 외부 클래스의 동작과 밀접한 관련은 있지만 인스턴스화와 무관한 경우
- 정적 유틸리티성 클래스 등에서 자주 사용됨
🔹 예제
public class Outer {
static int data = 100;
static class StaticNested {
void print() {
System.out.println("Data: " + data);
}
}
}
public class Main {
public static void main(String[] args) {
Outer.StaticNested nested = new Outer.StaticNested();
nested.print(); // Data: 100
}
}
2️⃣ member inner class (인스턴스 내부 클래스)
🔹 특징
- static이 아닌 내부 클래스
- 외부 클래스의 **모든 멤버(필드, 메서드 포함)**에 접근 가능
- 반드시 외부 클래스의 인스턴스를 통해 생성
🔹 주 용도
- 외부 클래스의 인스턴스와 밀접한 관계를 맺는 동작 구현 시
🔹 예제
public class Outer {
private String name = "OuterClass";
class Inner {
void show() {
System.out.println("Accessing: " + name);
}
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.show(); // Accessing: OuterClass
}
}
3️⃣ local inner class (지역 내부 클래스)
🔹 특징
- 메서드 내부에 선언된 클래스
- 선언된 메서드의 지역 변수처럼 동작
- 지역 변수에 접근할 경우, 해당 변수는 final 또는 effectively final이어야 함 (Java 8 이후)
🔹 주 용도
- 메서드 내에서만 임시적으로 필요한 구조적 로직 처리
- 간단한 캡슐화
🔹 예제
public class Outer {
void process() {
int factor = 5; // 실질적으로 final
class LocalInner {
void calculate() {
System.out.println("Result: " + (factor * 2));
}
}
LocalInner inner = new LocalInner();
inner.calculate(); // Result: 10
}
}
4️⃣ anonymous inner class (익명 내부 클래스)
🔹 특징
- 이름 없이 선언과 동시에 객체를 생성
- 보통 인터페이스나 추상 클래스의 구현체를 일회성으로 사용
- 단 한 번만 사용되는 간단한 동작 처리에 적합
🔹 주 용도
- 이벤트 핸들러, 콜백 구현
- Runnable, ActionListener 등의 인터페이스 구현 시
🔹 예제
public class Main {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Running thread via anonymous class");
}
});
t.start();
}
}
내부 클래스 컴파일 구조
내부 클래스는 컴파일되면 다음과 같이 .class 파일이 생성된다.
Outer.java
↓ 컴파일 후
Outer.class
Outer$Inner.class
Outer$StaticNested.class
Outer$1LocalInner.class
Outer$1.class (익명 클래스)
내부 클래스 사용 시 주의할 점
항목 | 내용 |
생성 | 인스턴스 내부 클래스는 외부 클래스 인스턴스를 통해 생성 |
메모리 누수 | 익명 클래스나 내부 클래스에서 외부 클래스의 참조를 오래 유지하면 메모리 누수 발생 가능 |
가독성 | 클래스가 너무 많아지면 오히려 구조가 복잡해질 수 있음 |
테스트 어려움 | 지역 및 익명 클래스는 테스트하기 어려움 (mocking 불가) |
익명 클래스 vs 일반 내부 클래스
항목 | 일반 내부 클래스 | 익명 내부 클래스 |
이름 | 있음 | 없음 |
생성 시점 | 명시적 생성 | 정의와 동시에 생성 |
목적 | 명확한 구조와 재사용 | 1회성 사용, 이벤트 처리 등 |
오버라이딩 가능한 수 | 여러 메서드 가능 | 1개의 인터페이스/클래스만 구현 가능 |
예외 처리 | 자유롭게 처리 가능 | 처리 범위 제한 |
실무 팁
- 정적 내부 클래스는 외부 클래스와 독립적인 유틸리티 성격의 객체 정의에 유용
- 인스턴스 내부 클래스는 외부 클래스의 상태를 공유해야 할 때 사용
- 익명 클래스는 람다식으로 대체 가능한 경우가 많으며, Java 8 이후 람다식을 우선 고려
- 지역 내부 클래스는 테스트가 어렵고 스코프가 좁아, 되도록 사용을 자제하는 편이 좋음
마무리 요약
구분 | 외부 클래스 인스턴스 필요 | static 접근 가능 | 이름 있음 | 사용 목적 |
static nested | ❌ | ✅ | ✅ | 정적 유틸성, 분리된 구성 |
member inner | ✅ | ✅ | ✅ | 외부 상태 공유, 캡슐화 |
local inner | ✅ (메서드 범위만) | ✅ | ✅ | 임시 구조화 |
anonymous inner | ✅ | ✅ | ❌ | 콜백, 이벤트, 일회성 처리 |
'자바 > 자바' 카테고리의 다른 글
(자바) 컬렉션 관련 정리 (자바의신 day9) (0) | 2025.07.06 |
---|---|
(JAVA) java.lang 패키지란 (0) | 2025.07.06 |
(자바)자바의 신 day6 - Java String 완벽 정리: intern(), 문자열 덧셈, 최적화 (0) | 2025.06.29 |
(자바) 자바의 신 day5 - 자바에서 업캐스팅과 다운캐스팅, equals와 hashCode, toString의 역할 및 오버라이딩 이유 (0) | 2025.06.25 |
(자바) 스프링과 리플렉션 정리2 (0) | 2025.06.25 |