CodeGym /행동 /JAVA 25 SELF /정적 중첩 클래스(static nested)

정적 중첩 클래스(static nested)

JAVA 25 SELF
레벨 16 , 레슨 1
사용 가능

1. 정적 중첩 클래스

정적 중첩 클래스 (static nested class)는 다른 클래스 내부에 static 한정자와 함께 선언된 클래스입니다. 본질적으로는 다른 클래스 안에 존재할 뿐, 그 클래스의 객체와는 연결되어 있지 않은 일반 클래스입니다.

내부 클래스(inner)가 항상 외부 클래스의 객체 손을 잡고 다니는 동생 같다면, 정적 중첩 클래스는 명절에만 찾아오는 사촌처럼 나머지 시간에는 독립적으로 살아갑니다.

핵심 차이점:

  • 외부 클래스 객체에 대한 암시적 참조가 없습니다OuterClass.this도 없고 비정적 멤버에 대한 접근도 없습니다.
  • 정적 멤버를 가질 수 있습니다 (일반 내부 클래스와 달리).
  • 외부 클래스의 객체 없이 생성됩니다.

선언 구문

정적 중첩 클래스 선언은 아주 간단합니다. 외부 클래스 안에서 static 키워드를 사용하면 됩니다.

class Outer {
    static class Nested {
        void print() {
            System.out.println("Hello from Nested!");
        }
    }
}

이게 전부입니다! 복잡한 문법은 없습니다 — 그냥 static class.

시각화:

Outer (외부 클래스)
│
├── Nested (static nested class)
│      └── print()

2. 정적 중첩 클래스 인스턴스 만들기

가장 좋은 점: 외부 클래스의 객체가 필요 없습니다!

Outer.Nested nested = new Outer.Nested();
nested.print(); // Hello from Nested!

주의: 클래스의 정규 이름 — Outer.Nested — 을 사용합니다. 중첩 클래스를 성씨로 부르는 것과 비슷합니다: "Smith.Son".

내부(inner) 클래스와 비교:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // outer 객체가 필요함

하지만 static nested의 경우에는 Outer 객체가 전혀 필요하지 않습니다!

3. 외부 클래스 멤버 접근

여기가 내부 클래스와의 핵심 차이입니다.

  • 정적 중첩 클래스는 외부 클래스의 정적 멤버에만 접근할 수 있습니다.
  • 비정적 필드와 메서드에는 접근할 수 없습니다(설령 public일지라도).

예시:

class Outer {
    private static int staticValue = 10;
    private int instanceValue = 20;

    static class Nested {
        void show() {
            System.out.println("Static value: " + staticValue); // OK
            // System.out.println("Instance value: " + instanceValue); // 오류!
        }
    }
}

비정적 필드 instanceValue에 접근하려고 하면, 컴파일 오류가 발생합니다.

왜 그럴까요? static nested class는 자신의 Outer 객체가 무엇인지 “알지” 못합니다 — 외부 클래스 객체에 대한 참조가 없기 때문입니다.

4. 언제 정적 중첩 클래스를 사용하나

언제 적절한가?

  • 중첩 클래스가 외부 클래스와 논리적으로 연관되어 있지만, 외부 클래스의 객체에 접근할 필요가 없을 때.
  • 보조 구조를 캡슐화하고 싶을 때: 예를 들어 builder, 유틸리티, enum, 혹은 작은 불변(immutable) 객체.
  • 패키지 오염을 줄이고 싶을 때: 해당 클래스가 외부 클래스에서만 쓰이고 바깥으로 뺄 필요가 없을 때.

전형적인 시나리오:

  • 빌더(Builder) 패턴 (특히 불변 객체)
  • 보조 자료구조 구현: 예를 들어 컬렉션의 내부 Node 클래스
  • 상수 묶음 또는 유틸리티

간단한 선택 규칙
스스로에게 물어보세요: "내 중첩 클래스가 외부 클래스의 특정 객체에 접근해야 하는가?"
아니요static class(정적 중첩)을 사용하세요
→ 일반 class(내부 클래스)를 사용하세요

5. 사용 예

예제 1: 클래스용 Builder

Person 클래스가 있고, 여기에 Builder 패턴을 적용해 보겠습니다:

public class Person {
    private final String name;
    private final int age;

    // 비공개 생성자
    private Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    // 정적 중첩 클래스 Builder
    public static class Builder {
        private String name;
        private int age;

        public Builder setName(String name) {
            this.name = name;
            return this;
        }

        public Builder setAge(int age) {
            this.age = age;
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }

    public void printInfo() {
        System.out.println("Person: " + name + ", " + age);
    }
}

사용:

Person person = new Person.Builder()
    .setName("Ivan")
    .setAge(30)
    .build();

person.printInfo(); // Person: Ivan, 30

왜 Builder가 정적 중첩 클래스일까요?
그것은 Person 객체에 의존하지 않고, 단지 그것을 만드는 데 도움만 주기 때문입니다. Person과 논리적으로 연관되어 있지만, 특정 인스턴스와는 무관합니다.

예제 2: 컬렉션 내부의 보조 구조

정수를 담는 “숫자 상자” 클래스를 가정해 보겠습니다. 요소 저장에는 내부 정적 클래스 Node를 사용합니다:

public class IntBox {
    private Node head;

    // 중첩 static class
    private static class Node {
        int value;
        Node next;

        Node(int value) {
            this.value = value;
        }
    }

    public void add(int value) {
        Node node = new Node(value);
        node.next = head;
        head = node;
    }

    public void printAll() {
        Node current = head;
        while (current != null) {
            System.out.println(current.value);
            current = current.next;
        }
    }
}

사용:

IntBox box = new IntBox();
box.add(1);
box.add(2);
box.add(3);
box.printAll(); // 3 2 1

왜 Node는 static일까요?
Node는 상자(IntBox) 전체를 알 필요가 없습니다. 단지 데이터를 담고 다음 Node를 가리키면 됩니다.

예제 3: 메인 클래스 내부의 유틸리티 클래스

public class MathUtils {
    // 복소수 작업용 정적 중첩 클래스
    public static class Complex {
        private final double re;
        private final double im;

        public Complex(double re, double im) {
            this.re = re;
            this.im = im;
        }

        public Complex add(Complex other) {
            return new Complex(this.re + other.re, this.im + other.im);
        }

        @Override
        public String toString() {
            return re + " + " + im + "i";
        }
    }
}

사용:

MathUtils.Complex a = new MathUtils.Complex(1, 2);
MathUtils.Complex b = new MathUtils.Complex(3, 4);
MathUtils.Complex sum = a.add(b);
System.out.println(sum); // 4.0 + 6.0i

6. 유용한 팁

내부 클래스 vs 정적 중첩 클래스

내부(inner) 클래스 정적 중첩(static nested)
키워드 없음
static
외부 객체에 대한 암시적 참조 아니요
외부 클래스의 비정적 멤버 접근 아니요
외부 클래스의 정적 멤버 접근
정적 멤버 포함 가능 아니요 (상수만)
생성 구문
outer.new Inner()
new Outer.Nested()
사용 시점 외부 클래스의 객체 접근이 필요할 때 외부 클래스의 객체 접근이 필요하지 않을 때

일러스트

flowchart LR
    OuterClass -->|has| InnerClass
    OuterClass -.->|has| StaticNestedClass
    StaticNestedClass -.->|can access| staticMembers
    InnerClass -->|can access| instanceMembers
    InnerClass -->|can access| staticMembers

특징 및 제약

  • Static nested class는 인스턴스 필드/메서드와 정적 필드/메서드를 모두 포함할 수 있습니다.
  • public, private, protected, package-private 등 어떤 접근 제어자로도 선언할 수 있습니다.
  • 인터페이스를 구현하고 다른 클래스를 상속할 수 있습니다.
  • generic일 수 있습니다.
  • 보통 외부 클래스 밖에서는 필요 없는 내부용 클래스를 캡슐화하는 데 사용합니다.

제네릭 static nested class 예시:

public class Box {
    public static class Holder<T> {
        private T value;
        public Holder(T value) { this.value = value; }
        public T get() { return value; }
    }
}

static nested class를 사용하지 말아야 할 때

  • 중첩 클래스가 외부 클래스의 비정적 필드/메서드에 접근해야 한다면 — 일반 inner class를 사용하세요.
  • 그 클래스가 외부 클래스 밖에서도 필요하다면 — 별도의 파일로 분리하세요.
  • 클래스가 너무 크거나 복잡하다면 — 역시 별도로 만드는 편이 좋습니다.

7. 흔한 실수와 주의점

오류 1: inner와 static nested 클래스를 혼동.
많은 초보자가 static nested class에서 외부 클래스의 비정적 필드에 접근하려고 합니다. 하지만 static nested class에는 외부 클래스 객체에 대한 참조가 없으므로 불가능합니다. 특정 객체의 상태에 접근해야 한다면 — 일반 inner class를 사용하세요.

오류 2: 외부 클래스 객체로 static nested class를 생성하려고 함.
outer.new Inner()처럼 쓸 필요가 없습니다. static nested class에서는 항상 new Outer.Nested()를 사용하세요.

오류 3: 외부 클래스 인스턴스에 대한 접근이 필요한 로직에 static nested class 사용.
클래스의 로직이 외부 클래스 객체의 상태와 밀접하다면, static nested class는 좋지 않은 선택입니다. 일반 내부 클래스를 사용하세요.

오류 4: 지나치게 복잡한 중첩.
중첩 클래스를 남용하지 마세요. 구조가 복잡해지면 일부 클래스를 바깥으로 분리하는 것이 좋습니다.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION