자바/자바

(자바) 내부 클래스란? (자바의 신 day7)

불광동 물주먹 2025. 7. 1. 00:18

내부 클래스란?

자바에서 클래스 안에 정의된 또 다른 클래스를 **내부 클래스(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 콜백, 이벤트, 일회성 처리