Java

[Java] Nested Class(중첩 클래스) - Static Nested Class, Inner Class, Anonymous Class, Local Class

림 림 2021. 9. 19. 00:41
반응형

 

다음 오라클 공식 문서와 Effective Java 서적을 참고하여 공부했습니다.

https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

 

Nested Classes (The Java™ Tutorials > Learning the Java Language > Classes and Objects)

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com

 

Index

1. Nested Class(중첩 클래스)란?

2. 중첩 클래스를 사용할 때의 주의사항

3. 중첩 클래스의 종류

4. Inner Class(내부 클래스)

5. Local Class(지역 클래스)

6. Anonymous Class(익명 클래스)

7. Static Nested Class(정적 중첩 클래스)

8. Static Nested Class와 Inner Class 비교

9. 메모리 누수

 

 

1. Nested Class(중첩 클래스)란?

중첩 클래스란 흔히 멤버 클래스로도 알려져 있습니다. 클래스 안에 멤버로 정의된 다른 클래스를 말합니다. 여기서 멤버로 '선언된' 클래스가 아니라 '정의된' 클래스임에 집중해야 합니다.

class OuterClass {
    ...
    class NestedClass {
        ...
    }
}

위의 코드에서, NestedClass 클래스가 중첩 클래스에 해당합니다.

 

 

2. 중첩 클래스를 사용할 때의 주의사항

중첩 클래스는 자신을 감싸고 있는 클래스에서만 쓰여야 합니다. 만약 그렇지 않고 외부의 다른 클래스에서도 사용되어야 한다면, 중첩 클래스로 정의하지 말고 외부의 클래스로 따로 정의해야 합니다. 

 

 

3. 중첩 클래스의 종류

중첩 클래스는 먼저 두 종류로 나뉘어집니다.

  • Static(정적): Static Nested Class
  • Non-static(비정적): Inner Class

static 키워드로 선언된 정적 클래스는 Static Nested Class(정적 중첩 클래스)라고 합니다.

비정적 클래스는 Inner Class(내부 클래스)라고 합니다. 비정적 클래스는 다시 비정적 중첩 클래스, 익명 클래스, 지역 클래스의 세 가지의 클래스로 나뉘어집니다. 

그래서 중첩 클래스는 이렇게 총 네 가지의 클래스로 분류할 수 있습니다.

  • Static Nested Class(정적 중첩 클래스)
  • Non-static Nested Class(비정적 중첩 클래스)
  • Anonymous Class(익명 클래스)
  • Local Class(지역 클래스)

 

 

4. Inner Class(내부 클래스)

public class OuterClass {
	...
    class InnerClass {
        ...
    }
}

특징

InnerClass는 static 키워드 없이 선언됩니다.

InnerClass는 OuterClass에 선언된 모든 멤버에 접근할 수 있습니다. private로 선언된 멤버에도 접근할 수 있습니다.

InnerClass는 static 멤버를 선언할 수 없습니다. 

InnerClass의 인스턴스는 항상 OuterClass의 인스턴스와 함께 OuterClass의 내부에 존재합니다. (암묵적으로 OuterClass 인스턴스와 연결됩니다.)

 

내부 클래스의 객체 생성 방법

내부 클래스는 항상 자신을 감싸는 클래스의 객체와 함께 존재하기 때문에, 내부 클래스의 객체를 생성하기 위해서는 먼저 외부 클래스의 객체를 생성해야 합니다. 그리고 나서 내부 클래스의 객체를 생성해줍니다.

OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();

 

 

5. Local Class(지역 클래스)

위에서 중첩 클래스의 종류를 살펴보면서, 내부 클래스에는 익명 클래스와 지역 클래스가 포함된다고 했습니다. 먼저 지역 클래스에 대해 알아보겠습니다.

Local Class란, block에 정의된 클래스를 말합니다. 여기서 말하는 block이란, 여러 statements를 감싸고 있는 하나의 중괄호 쌍을 의미합니다. 예를 들어, Local Class는 메서드, for문, if문 등의 모든 block에서 선언될 수 있습니다.

public class LocalClassExample {
    ...
    public void methodExample() {
        ...
        class LocalClass {
            ...
        }
    }
}

 

특징

Local Class는 자신을 감싸고 있는 block의 모든 멤버에 접근할 수 있습니다.

비정적 문맥에서 사용될 때만 Outer Class 인스턴스를 참조할 수 있습니다.

Local Class는 static 멤버를 가질 수 없습니다.

다음과 같이 final static 키워드가 붙은 상수는 선언할 수 있습니다.

public class LocalClassExample {

    public void methodExample() {

        class LocalClass {
            public static final int MAX = 10;
            public static final String F = "female";
        }
    }
}

 

 

6. Anonymous Class(익명 클래스)

익명 클래스는 이름이 없는 지역 클래스와 같다고 볼 수 있습니다. 익명 클래스는 선언과 동시에 초기화가 이루어집니다.

익명 클래스의 한 가지 예로는 다음과 같이 GUI가 있는 애플리케이션에서 컴포넌트에 이벤트 리스터를 등록할 때 쓰입니다.

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        ...
    }
}

익명 클래스는 코드를 더 간결하게 만들어줍니다. 로컬 클래스가 한번만 사용되어진다면 익명 클래스를 사용하는 것이 좋습니다.

특징

Anonymous Class는 Outer Class의 지역 변수가 final로 선언되어야 접근할 수 있습니다.

비정적인 문맥에서 사용될 때만 Outer Class의 인스턴스를 참조할 수 있습니다.

Local Class와 같이 static 멤버를 가질 수 없습니다.

멤버 인터페이스를 선언할 수 없습니다.

 

 

7. Static Nested Class(정적 중첩 클래스)

지금까지 Inner Class와 Inner Class의 일종인 Local Class, Anonymous Class에 대해 알아보았습니다. 이번엔 Static Nested Class에 대해 알아보겠습니다.

Static Nested Class는 static 키워드와 함께 선언된 정적 중첩 클래스입니다. 

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
}

 

특징

static 키워드와 함께 선언됩니다.

자신을 감싸고 있는 클래스의 멤버에 접근할 수 없습니다.

자신을 감싸고 있는 클래스의 static 멤버에만 접근할 수 있습니다.

static이기 때문에, JVM 클래스 로딩 매커니즘에 의해 클래스 로딩 시점에 한 번만 호출됩니다. 

 

 

8. Static Nested Class와 Inner Class 비교

Static Nested Class와 Inner Class는 둘 다 클래스 안에 선언된 클래스라는 점에서 공통점이 있습니다.

그리고 구문상으로는 다음과 같이 static이 쓰였는지 아닌지의 차이만 있지만, 의미상 차이는 꽤 큽니다.

class OuterClass {
    ...
    class InnerClass {
        ...
    }
    static class StaticNestedClass {
        ...
    }
}

 

차이점 1) Outer Class의 멤버에 접근

다음과 같이 IntelliJ에서 코드를 작성해 보았습니다.

line:15에서 'Non-static field 'outerField' cannot be referenced from a static context'라는 메세지와 함께 컴파일 에러가 발생합니다.

Static Nested Class는 Outer Class의 static 멤버에만 접근할 수 있다(private 포함)는 것을 알 수 있습니다.

반면에 Inner Class는 Outer Class의 모든 멤버에 접근할 수 있습니다.(private 포함)

 

Static Nested Class에서 Outer Class의 필드에 접근하기 위해서는 다음과 같이 Outer Class의 인스턴스를 참조하여야 합니다.

 

차이점2) 정규화된 this의 사용

다음과 같이 Inner Class에서 x, this.x, OuterClass.x의 값을 알아보는 코드를 작성했습니다.

main 메서드에서 testShadowing 메서드를 실행해보면 각각 다음과 같은 값이 출력됩니다.

- x: testShadowing 메서드의 매개변수로 전달된 값

- this.x: 1

- OuterClass.this.x: 0

Inner Class의 인스턴스에서는 정규화된 this를 통해서 Outer Class의 참조를 가져오거나 메서드를 실행할 수 있습니다. 정규화된 this란 클래스명.this 와 같이 Outer Class의 이름을 명시하는 것을 말합니다.

반면에, 다음 코드와 같이 Static Nested Class에서는 정규화된 this를 사용하려고 하면 컴파일 에러가 발생합니다.

 

차이점 3) 인스턴스의 독립적 존재

Inner Class에서 Outer Class의 정규화된 this를 사용할 수 있는 이유는, Inner Class의 인스턴스는 항상 Outer Class의 인스턴스와 함께 존재하고, 서로 연결되기 때문입니다. Inner Class의 인스턴스는 Outer Class 인스턴스 없이는 생성할 수 없습니다.

반면에, Static Nested Class의 인스턴스는 독립적으로 존재할 수 있습니다. 

그렇기 때문에, 개념상 Nested Class의 인스턴스가 Outer Class의 인스턴스와 독립적으로 존재할 수 있다면 Static Nested Class로 선언해야 합니다.

 

 

9. 메모리 누수

개발자로서 메모리를 관리하는 것은 굉장히 중요한 일입니다. 목적에 맞는 중첩 클래스를 선언해야 메모리 누수와 메모리 낭비를 막을 수 있습니다.

static 키워드를 생략한 Inner Class를 선언하면 Outer Class으로의 참조를 갖게 됩니다. 참조를 저장하려면 시간과 공간적인 측면에서 비용이 발생합니다. 그리고 가비지 컬렉션이 Outer Class 인스턴스를 수거하지 못하면 메모리 누수가 발생할 수 있습니다.

따라서 Nested Class에서 Outer Class에 접근할 일이 없다면 static을 붙여서 Static Nested Class로 선언하는 것이 가장 좋습니다.

 

반응형