ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java: 내부 클래스(Inner Class)의 기본적인 이해
    각종 학습 요약/Java 2022. 5. 18. 11:41

    Java: 내부 클래스(Inner Class)의 기본적인 이해

    Inner Class란?


    말 그대로, 클래스 안에 정의된 클래스다. 내부 클래스를 감싸고 있는 클래스는 상대적으로 외부 클래스라고 불리게 된다.
    클래스 안에 정의되는 클래스다보니, 아무래도 바깥의 클래스와 어떤 연관을 가지느냐를 중요히 생각해볼 필요가 있다.
    내부 클래스의 유형은 네 가지가 있는데, 이제 아래의 설명에서 하나씩 천천히 알아가보자.

    참고 : 아래의 설명에서 Inner Class, 이너클래스, 내부클래스, nested class, 중첩클래스라는 말을 혼용하게 될 것 같다. 당연히 전부 같은 의미다.

    내부 클래스의 유형: 먼저 크게 두가지로 - 인스턴스 내부클래스/스태틱 내부클래스


    "클래스 안의 클래스"라는 설명을 들었을 때, 보통 쉽게 생각할 수 있는 이너클래스의 형태는 이럴 것 같다.

    
    class 외부클래스 {
    
        class 내부클래스1 {
            int 어떤값;
        }
    
        static class 내부클래스2 {
            static String 다른값 = "여긴 내부클래스에요";
        }
    
    }
    

    누가 봐도 내부 클래스인 두 녀석이다.
    내부클래스1의 경우에는 인스턴스 이너클래스라고 불리는 종류고, 내부클래스2스태틱 이너클래스라고 불린다.

    구분 인스턴스 이너클래스 스태틱 이너클래스
    위치 외부 클래스 바로 안쪽에 위치 외부 클래스 바로 안쪽에 위치
    차이점 외부 클래스의 인스턴스 멤버/스태틱 멤버를 사용할 수 있다. 외부 클래스의 스태틱 멤버를 사용할 수 있다.
    사용처 사실 외부클래스 하나 더 작성하는 거랑 별 차이 없음. 내부클래스가 필요한 경우, 외부 클래스의 인스턴스를 직접 참조하는 코드가 없다면, 스태틱 이너클래스로 만들어주는 편이 좋다.(출처: 이펙티브 자바 24장)

    인스턴스와 스태틱 사이에 왜 이런 차이가 일어나는지 아직 모르겠다면 생성자를 설명하는 포스팅의 일부분을 참고('여담 3.' 참고)해보자('생성된' 자원에만 접근할 수 있다...는 사고에 익숙해지자).

    내부 클래스의 큰 두 갈래는 이 두가지였다. 인스턴스 내부클래스와 스태틱 내부클래스. 그래서 이 둘을 묶어서 멤버 내부클래스라고 부르기도 한다(정의되는 위치가, 딱 멤버변수가 정의되는 그 위치니까).
    접근하는 방식도 똑같다.

    // 이런 식으로
    외부클래스.내부클래스1.어떤값 = 3; 
    // 또는 이런 식으로
    System.out.println(외부클래스.내부클래스2.다른값); // => "여긴 내부클래스에요"

    그리고 아래의 설명에서 등장할 나머지 내부클래스들은 사실상 인스턴스 내부클래스의 하위 개념이라고 보면 된다.

    나머지 내부클래스들은 무엇이 있을까? - 1. 로컬 내부클래스


    일단 이름부터 들어보고 알아보자.
    '로컬 내부클래스'가 있고 '익명 내부클래스'가 있다.

    '멤버 내부클래스'들이 멤버 영역에 정의된 내부클래스였던 것처럼, '로컬 내부클래스'는 로컬 영역에 정의된 내부클래스다.
    로컬이 어디였냐...생각을 해보면은. 메소드 안에 정의된 변수들을 '로컬 변수'라고 불렀던 기억이 금방 떠오를 것이다.
    맞다. '지역 내부클래스'는 메소드 안에서 정의가 된다. 이렇게 말이다.

    class 외부클래스 {
    
        public int 외부인스턴스변수 = 3;
    
        public void 로컬메소드() {
            int 로컬변수 = 5;
    
            class 로컬_내부클래스 { /* 여기가 로컬내부클래스다. */
                int 로컬_내부클래스_멤버1;
                int 로컬_내부클래스_멤버2 = 외부인스턴스변수;
            }
    
            로컬_내부클래스 inner = new 로컬_내부클래스(); // 이런식으로 생성해서
            inner.로컬_내부클래스_멤버1 = 로컬변수; // 이런식으로 쓸 수 있다.
            System.out.println(inner.로컬_내부클래스_멤버2); // => 5
    
        }
    
    }

    로컬변수와 같은 영역에서 사용되기 때문에 로컬변수와 성질이 비슷하다.

    • 정의된 메소드 밖에서 사용할 수 없고, 정의된 메소드 안에서 'new'키워드로 생성한 뒤 사용해야 한다.
      'new를 사용해야한다(혹은 생성한다)'는 것을 읽으면 바로 '인스턴스'라는 개념을 떠올려야 한다! 거기에 따라오는 특징은...
    • 인스턴스 내부클래스와 마찬가지로, 외부 클래스의 인스턴스 멤버/스태틱 멤버에 접근할 수 있다.
    • 메소드 안에서 가능했듯이, 생성자 안에서도 사용이 가능하다.

    혹시 (아직도)이런 의문이 들 수도 있다.
    메서드 내에서 클래스를 정의할 수 있으면, 메서드 안에 '로컬 스태틱 클래스'를 만들 수 있을까? 답은 '안된다'.
    스태틱 메서드 내에서는 할 수 있지 않을까? 답은 '안된다'.
    얼핏 생각하면 두번째는 될 수도 있을 것 같지만, 자바 컴파일러는 둘 다 동일하게 illegal start of expression을 발생시킨다.

    나머지 내부클래스들은 무엇이 있을까? - 2. 익명 내부클래스


    이름이 없다는 게 무슨 뜻일까? 클래스를 선언하려면 이름이 있어야 하는데 말이다.
    하지만 반대로 생각해보면, 그건 클래스 선언을 안 하면 이름이 없어도 된다는 의미다.
    클래스 선언을 안하고... 내용물만 따로 쓸 수 있다면?

    class 외부클래스 {
    
        void 로컬메소드() {
    
            int 로컬변수1 = 1;
            int 로컬변수2 = 2;
            String 로컬변수3 = "hello world";
    
            AnonyClass anoCls = new AnonyClass() { // 이렇게 말이다.
                int 익명내부인스턴스변수 = 100;
    
                void compare(int o1, o2) {
                    /* 쿵짝쿵짝 */
                }
    
            };
    
            anoCls.compare(로컬변수1, 로컬변수2); // 사용은 이렇게 한다.
    
        }
    
    }

    선언도 하지 않고, 생성자도 따로 만들지 않고, 정의와 동시에 생성한뒤 사용하고 나서 사용을 마치면 사라진다. 사실상 가장 많이 접하는 유형의 중첩클래스다.
    익명 내부클래스 역시 인스턴스 내부클래스/로컬 내부클래스와 마찬가지로, 외부 클래스의 인스턴스 멤버/스태틱 멤버에 접근할 수 있다.

    댓글

Designed by Tistory.