Java - 중첩, 내부 Class

develkkm·2025년 12월 18일

0부터 시작하는 Java

목록 보기
26/27

자바에서는 클래스 안에 또 다른 클래스를 정의할 수 있다.
이런 구조를 중첩 클래스라고 하며 클래스의 선언 위치와 바깥 클래스와의 관계에 따라 여러 종류로 나뉜다.
중첩 클래스는 단순한 문법 기능이 아니라 캡슐화와 역할 분리를 강화하기 위한 설계 도구에 가깝다.


중첩 클래스

중첩 클래스는 클래스 안에 정의된 클래스를 의미한다.
변수의 선언 위치에 따라 정적 변수 인스턴스 변수 지역 변수로 나뉘듯 중첩 클래스도 선언 위치에 따라 성격이 달라진다.

중첩 클래스는 크게 정적 중첩 클래스와 내부 클래스로 나뉜다.

정적 중첩 클래스는 바깥 클래스와 논리적으로만 묶여 있을 뿐 인스턴스 관점에서는 완전히 독립적인 클래스다.
내부 클래스는 바깥 클래스의 인스턴스에 소속되는 구성 요소다.


정적 중첩 클래스

정적 중첩 클래스는 static 키워드가 붙은 클래스다.
정적 변수와 같은 위치에 선언되며 바깥 클래스의 인스턴스와는 아무런 관계가 없다.

class Outer {
    static class StaticNested {
        void hello() {
            System.out.println("hello");
        }
    }
}

정적 중첩 클래스는 다음과 같은 특징을 가진다.

  • 자신의 멤버에 접근할 수 있다.
  • 바깥 클래스의 인스턴스 멤버에는 접근할 수 없다.
  • 바깥 클래스의 정적 멤버에는 접근할 수 있다.

생성 시 바깥 클래스의 인스턴스가 필요 없다.

Outer.StaticNested nested = new Outer.StaticNested();
nested.hello();

정적 중첩 클래스는 사실상 별도의 클래스를 하나 더 만든 것과 크게 다르지 않다.
다만 같은 클래스 파일 안에 있기 때문에 바깥 클래스의 private 멤버에 접근할 수 있다는 점이 차이다.

주로 바깥 클래스 안에서만 사용되는 보조 클래스를 표현할 때 사용한다.
외부에 노출할 필요가 없는 개념을 논리적으로 묶는 용도에 적합하다.


내부 클래스

내부 클래스는 static 이 붙지 않은 중첩 클래스다.
바깥 클래스의 인스턴스에 소속되는 구성 요소로 취급된다.

class Outer {
    private int value = 10;

    class Inner {
        void print() {
            System.out.println(value);
        }
    }
}

내부 클래스는 바깥 클래스의 인스턴스 멤버에 직접 접근할 수 있다.
이는 내부 클래스가 바깥 인스턴스의 참조를 암묵적으로 보관하고 있기 때문이다.

내부 클래스는 바깥 클래스의 인스턴스 없이 생성할 수 없다.

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.print();

내부 클래스는 바깥 클래스의 구현 세부사항을 캡슐화하는 데 매우 유용하다.
바깥 클래스 내부에서만 사용되는 로직을 외부로 노출하지 않고 감출 수 있다.


지역 클래스

지역 클래스는 메서드나 코드 블록 내부에 선언된 클래스다.
내부 클래스의 성격을 그대로 가지면서 지역 변수에 접근할 수 있다는 특징이 있다.

class Outer {
    void process(int param) {
        int local = 1;

        class LocalClass {
            void print() {
                System.out.println(local);
                System.out.println(param);
            }
        }

        LocalClass lc = new LocalClass();
        lc.print();
    }
}

지역 클래스는 다음 범위에 접근할 수 있다.

  • 자기 자신의 인스턴스 변수
  • 자신이 선언된 메서드의 매개변수와 지역 변수
  • 바깥 클래스의 인스턴스 멤버

다만 지역 클래스가 접근하는 지역 변수는 반드시 final 이거나 사실상 final 이어야 한다.


지역 변수 캡처

지역 클래스는 자신이 사용하는 지역 변수의 값을 인스턴스 생성 시점에 복사해서 보관한다.
이 과정을 변수 캡처라고 한다.

지역 변수는 스택 영역에 존재하고 메서드 종료 시 함께 제거된다.
반면 지역 클래스로 생성한 객체는 힙 영역에 존재하며 더 오래 살아남을 수 있다.

이 생명주기 차이를 해결하기 위해 자바는 필요한 지역 변수의 값을 인스턴스 내부에 복사해 둔다.
그래서 메서드가 종료된 이후에도 지역 클래스는 캡처된 값을 사용할 수 있다.

class Outer {
    void process(int param) {
        int local = 10;

        class LocalClass {
            void print() {
                // 지역 변수 캡처
                System.out.println(local);
                System.out.println(param);
            }
        }

        LocalClass localClass = new LocalClass();
        localClass.print();

        // local = 20;  // 컴파일 오류 (캡처된 지역 변수는 변경 불가)
        // param = 30;  // 컴파일 오류
    }
}

지역 변수가 변경 가능하다면 스택의 값과 캡처된 값 사이에 불일치가 발생한다.
이 문제를 원천적으로 차단하기 위해 자바는 캡처 대상 지역 변수의 변경을 금지한다.


익명 클래스

익명 클래스는 이름이 없는 지역 클래스다.
선언과 생성을 동시에 수행하며 주로 일회성 구현에 사용된다.

Printer printer = new Printer() {
    @Override
    public void print() {
        System.out.println("hello");
    }
};

익명 클래스는 다음 특징을 가진다.

  • 클래스 이름이 없다.
  • 부모 클래스 상속 또는 인터페이스 구현이 반드시 필요하다.
  • 생성자를 정의할 수 없다.
  • 한 번만 인스턴스를 생성할 수 있다.

익명 클래스는 코드의 양을 줄이고 의도를 바로 드러내는 데 유리하다.
다만 구현이 복잡해지거나 재사용이 필요하다면 일반 클래스나 지역 클래스로 분리하는 것이 낫다.


중첩 클래스 사용 기준

중첩 클래스는 특정 클래스 내부에서만 사용되는 개념일 때 의미가 있다.
여러 클래스에서 재사용된다면 중첩 클래스로 만들면 안 된다.

중첩 클래스를 사용하는 이유는 두 가지다.

  • 논리적 그룹화로 코드 구조를 명확히 한다.
  • 불필요한 public 노출을 줄여 캡슐화를 강화한다.

정적 중첩 클래스는 독립적인 보조 개념에 적합하다.
내부 클래스와 지역 클래스는 바깥 클래스와 강하게 결합된 구현 요소에 적합하다.
익명 클래스는 일회성 동작을 빠르게 표현할 때 사용한다.

profile
알던것을 더 확실하게

0개의 댓글