HOWTO · Java

Java의 내부 클래스 및 정적 중첩 클래스

컴퓨터 프로그래밍에서 클래스는 데이터 멤버 또는 멤버 함수의 데이터에 액세스하기 위해 개별 클래스 개체를 만들 수 있는 청사진입니다. Java 프로그래밍에서 클래스는 키워드 class로 정의됩니다. 또한 클래스는 사용자 정의 수정이 포함된 사용자 정의 데이터 유형이라고 말할 수 있습니다.

이 페이지의 내용

중첩 클래스는 다른 클래스 안에 선언하는 클래스입니다. Java는 static 중첩 클래스와 non-static 중첩 클래스(내부 클래스)를 지원합니다. 타입이 바깥 클래스와 개념적으로 관련되어 있지만 바깥 객체는 필요하지 않을 때 static 중첩 클래스를 사용합니다. static 키워드는 이러한 관계를 나타냅니다. 이 키워드가 중첩 타입을 최상위 클래스로 바꾸거나 보안 경계를 제공하는 것은 아닙니다.

Java의 정적 중첩 클래스

static 중첩 클래스는 다른 클래스 안에 static 키워드와 함께 선언하는 멤버 타입입니다. Envelope.Label처럼 바깥 타입을 붙여 이름을 지정할 수 있으며, Envelope 객체를 먼저 만들지 않고도 인스턴스화할 수 있습니다. 특정 클래스와 밀접하게 연관되어 별도의 최상위 타입으로 두는 것이 적절하지 않은 보조 타입이나 값 타입에 유용합니다.

중첩 타입에도 자체 인스턴스가 있습니다. 중첩 클래스를 static로 선언해도 해당 클래스의 필드나 메서드가 static가 되는 것은 아닙니다. 최상위 클래스와 마찬가지로 자체 생성자, 인스턴스 상태, 메서드를 정의할 수 있습니다.

바깥 클래스와 정적 중첩 클래스의 관계

static 중첩 클래스에는 바깥 클래스 인스턴스에 대한 암시적 참조가 없습니다. 바깥 클래스의 static 멤버에는 private static 멤버를 포함해 직접 접근할 수 있습니다. 하지만 연결된 바깥 객체가 없으므로 바깥 인스턴스의 필드나 메서드를 직접 사용할 수는 없습니다. 그런 데이터가 필요하면 바깥 객체를 생성자나 메서드에 전달하고, 그 참조를 통해 멤버에 접근하세요.

반대 방향의 접근 관계도 유용합니다. 바깥 클래스의 코드는 Java의 일반 접근 규칙에 따라 중첩 클래스의 private 멤버를 포함한 멤버를 사용할 수 있습니다. private 가시성은 어떤 코드가 멤버의 이름을 사용할 수 있는지를 제한할 뿐이며, 보안 기능으로 설명해서는 안 됩니다.

정적 중첩 클래스 선언 및 인스턴스화

바깥 클래스 본문에 static class Label 형식으로 타입을 선언합니다. 바깥 클래스 외부에서는 Envelope.Label이라고 참조합니다. 다음 전체 예제는 중첩 값 타입을 사용하는 클래스와 함께 묶고, main에서 바로 생성합니다.

이 예제를 실행해 보세요:

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

public class Main {
    static class Label {
        private final String text;

        Label(String text) {
            this.text = text;
        }

        String display() {
            return "Label: " + text;
        }
    }

    public static void main(String[] args) {
        Label label = new Label("priority");
        System.out.println(label.display());
    }
}
Label: priority

main은 static 메서드이므로 Main을 생성하지 않고도 Label을 만듭니다. 변경하지 않은 예제의 출력 레이블은 영어로 Label: priority라고 표시됩니다. 다른 클래스의 코드에서는 한정된 이름인 Main.Label을 사용하고 접근 가능한 생성자를 호출할 수 있습니다. 중첩 클래스에 특정 바깥 객체의 정보가 필요하다면 그 객체를 전달해 의존성을 명시적으로 표현하세요.

정적 중첩 클래스와 내부 클래스 비교

내부 클래스는 non-static 멤버 클래스입니다. 각 내부 클래스 객체는 바깥 클래스의 인스턴스와 연결되며, 내부 클래스의 코드는 해당 바깥 인스턴스의 멤버를 직접 참조할 수 있습니다. 바깥 클래스 외부에서 내부 클래스를 만들려면 먼저 바깥 객체를 만든 다음 outer.new Inner() 형식을 사용합니다. 내부 타입의 이름은 Outer.Inner입니다.

static 중첩 클래스에는 그런 연결이 없으므로 생성할 때 바깥 객체가 필요하지 않습니다. 외부에서는 new Outer.Nested()를 사용하고, 범위 안에서는 짧은 이름을 사용할 수 있습니다. 의도한 API에 따라 두 타입 모두 private 또는 public으로 선언할 수 있으며, 둘 다 바깥 클래스의 private 멤버에 접근할 수 있습니다. 차이는 암시적인 바깥 인스턴스 관계에 있으며, 특별한 보안 수준의 차이가 아닙니다.

정적 중첩 클래스를 사용할 때

중첩 타입이 논리적으로 바깥 클래스의 일부이면서 각 인스턴스가 암시적인 바깥 객체 없이 작업할 수 있다면 static 중첩 클래스를 선택하세요. 작은 값 타입, 빌더, 특정 바깥 API에만 의미가 있는 보조 타입이 흔한 예입니다. static 중첩 클래스도 의존성을 명시적으로 전달받을 수 있어 호출자가 필요한 조건을 분명히 알 수 있습니다.

각 인스턴스가 자신을 만든 특정 바깥 객체와 함께 동작해야 한다면 내부 클래스를 선택하세요. 예를 들어 그 객체의 인스턴스 상태를 읽거나 갱신해야 하는 경우입니다. 타입이 폭넓게 독립적인 역할을 한다면 최상위 클래스가 재사용과 발견에 더 편리할 수 있습니다. 코드에 필요한 관계를 기준으로 선택한 뒤, 의도한 사용을 지원하는 범위에서 가장 제한적인 가시성을 지정하세요.