[Java] 중첩클래스 (내부클래스, 멤버클래스)

2023. 10. 20. 11:47프로그래밍/Java

Java에서는 클래스 내부에 클래스 혹은 인터페이스를 중첩해서 사용할 수 있게 해준다.

이를 중첩 클래스라고 하고 인터페이스의 경우에는 내부 인터페이스/ 중첩 인터페이스라고 명명하는 듯 하다.

이 글에서는 중첩 클래스에 대한 간단한 2가지 경우만 살펴본다.

중첩클래스는 다음으로 나뉘어진다.

1. 멤버 클래스(Member Class)

2. 지역 클래스(Local Class)

 

이름 그대로의 뜻을 가지고 있다. 멤버는 말그대로 중첩클래스가 클래스의 가장 바깥쪽 중괄호 바로 안에 위치함으로써 멤버(필드)의 역할을 하는 것이다. 지역 클래스는 우리가 익히 알고 있는 지역 변수, 지역 변수의 스코프와 동일하다. 즉 지역 메서드 내에 선언돼서 해당 메서드 내에서만 객체를 생성하고 사용할 수 있는 것이 지역클래스이다.

 

- 멤버클래스

package midtermexam;


public class MemberDemo{

    String name = "outer nagnebro";
    MemberClass m = new MemberClass();
    class MemberClass{

        String name = "yeongchan";

        void y(){

            System.out.println(name);
            System.out.println(MemberDemo.this.name);
        }
    }

}

class MemberDemoApplication{

    public static void main(String[] args) {

        MemberDemo m = new MemberDemo();
        MemberDemo.MemberClass test = m.new MemberClass();

        test.y();

    }
}

멤버 클래스의 경우에는 클래스의 멤버, 필드들과 같은 위치에 클래스를 생성하는 것이다. main메서드의 실행 결과를 예측해보자. 

test.y()의 실행결과는 MemberClass의 name이 가르키는 yeongchan, MemberDemo클래스의 name인 outer nagnebro가 출력된다. 기본적으로 변수와 함수의 스코프에 대한 개념을 이해하고 있다면 굉장히 쉬운 예제이다.

main메서드에서 MemberClass의 객체를 생성하는 방법이 우리가 알던 방법과는 조금 다르다. 이는 MemberDemo내부에 있는 MemberClass라는 것을 명시적으로 표기해줘야 하기 때문이다. 이것은 클래스타입의 명시에 대한 이야기이고 인스턴스를 생성할 때는 또 다르다. 변수를 생성할 때 좌측의 클래스 타입과 우측의 인스턴스 타입이 서로 어떤 관계인지를 이해하고 있다면 우측의 m.new MemberClass() 코드도 이해가 갈 것이다. 어쨌든 MemberClass는 MemberDemo안에 있기 때문에 접근하기 위해서는 MemberDemo의 인스턴스가 필요하고 그 인스턴스(m)로 접근해서 객체를 생성할 수 있는 것이다. 

 

 

- 지역클래스

package midtermexam;

public class LocalDemo {

    String name = "yeongchan";

    void y() {

        class LocalClass {

            String name = "local";

            void localTest() {

                System.out.println("This is local method");
            }
        }

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


class LocalDemoApplication{

    public static void main(String[] args) {

        LocalDemo l = new LocalDemo();
        l.y();
    }
}

 

LocalDemo클래스이다. LocalClass는 void y()메서드 안에 정의돼있다. 일반적으로 생각해보자. 우리는 다른 메서드 안에 정의된 지역변수에 접근할 수 있었는가? return으로 값을 돌려받지 않는 이상 절대 불가능하다. 이와 마찬가지로 위의 LocalClass의 객체도 return값을 사용해 인스턴스를 돌려받는 것이 아니면 직접적으로 LocalClass에 접근할 방법은 없다. LocalClass를 간단하게 지역변수 하나라고 생각해보면 이해가 빠를 것이다. 

LocalClass는 y메서드 내에 선언돼있으므로 위의 예제의 main메서드에서 MemberDemo.MemberClass처럼 클래스를 명시하지 않고도 바로 접근이 가능하다. 

 

- 그렇다면 지역클래스와 멤버클래스를 사용하는 근본적인 이유는 무엇일까?

필자가 생각하기에는 코드의 가독성 및 간결함이다. 또한 클래스가 포함하고 있는 필드가 많지 않으면서, 상속관계로 설정하기에는 연관성이 조금 부족하고 객체가 가지고 있는 하나의 정보를 또 다시 객체화 하기 위해 사용하기에 좋은 듯하다.

예를 들어 나라는 사람(Person Class)이 가진 정보는 무수히 많다. 그리고 Person클래스의 필드에는 태어난 지역, 인적정보, 다니고 있는 기업 정보 등이 포함돼있을 것이다. 이러한 정보들은 하나의 문자열이 아니라 세분화 시킬 수도 있다. 인적정보는 이름이나 나이, 키, 몸무게 등의 정보를 포함하듯이 말이다. 이럴 경우 해당 클래스를 사람의 정보를 나타내는 PersonInformation이라는 새로운 클래스로 생성할 수도 있다. 그러나 생성해야 할 사람의 객체수가 많지 않고 자주 사용되지 않을 코드라면 그냥 Person클래스 내부에 간결하게 작성하는 것이 더 나을 수도 있다. 

 

예제가 정확하지는 않을지라도 조금 더 코드를 간결하고 직관적으로 작성하기에는 충분히 도움이 되는 방법인 듯 하다.