본문으로 바로가기

[Java] 중첩 클래스의 접근 제한

category Backend/Java 2022. 4. 27. 13:54

1. 클래스에서 중첩 클래스 사용 제한

  • 인스턴스 멤버 클래스는 클래스의 인스턴스 필드의 초기값이나 인스턴스 메소드에서 객체를 생성할 수 있습니다.
  • 인스턴스 멤버 클래스는 정적 필드의 초기값이나 정적 메소드에서는 객체를 생성할 수 없습니다.
public class OutterClass {
    // 인스턴스 필드
    NestedInstanceClass nestedInstanceClass1 = new NestedInstanceClass();
    NestedStaticClass nestedStaticClass1 = new NestedStaticClass();

    // 정적 필드
    // static NestedInstanceClass nestedInstanceClass2 = new NestedInstanceClass(); (x)
    static NestedStaticClass nestedStaticClass2 = new NestedStaticClass();

    // 인스턴스 메소드
    void instanceMethod() {
        NestedInstanceClass nestedInstanceClass = new NestedInstanceClass();
        NestedStaticClass nestedStaticClass = new NestedStaticClass();
    }

    // 정적 메소드
    static void staticMethod() {
        // NestedInstanceClass nestedInstanceClass = new NestedInstanceClass(); (x)
        NestedStaticClass nestedStaticClass = new NestedStaticClass();
    }

    //==인스턴스 멤버 클래스==//
    class NestedInstanceClass { }

    //==정적 멤버 클래스==//
    static class NestedStaticClass { }
}

 

2. 중첩 클래스에서 클래스 사용제한

멤버 클래스에서 사용제한

  • 정적 멤버 클래스 안에서는 바깥 클래스의 정적 필드와 메소드에만 접근할 수 있고 인스턴스 필드와 메소드는 접근할 수 없습니다.
public class OutterClass {
    // 인스턴스 멤버
    String instanceField;
    void instanceMethod() { }

    // 정적 멤버
    static String staticField;
    static void staticMethod() { }

    //==인스턴스 멤버 클래스==//
    class NestedInstanceClass {
        void testMethod() {
            System.out.println(instanceField);
            instanceMethod();
            System.out.println(staticField);
            staticMethod();
        }
    }

    //==정적 멤버 클래스==//
    static class NestedStaticClass {
        void testMethod() {
            // System.out.println(instanceField); (x)
            // instanceMethod(); (x)
            System.out.println(staticField);
            staticMethod();
        }
    }
}

 

로컬 클래스에서 사용제한

  • 로컬 클래스 내부에서는 바깥 클래스의 필드나 메소드를 제한 없이 사용할 수 있습니다.
public class OutterClass {
    // 인스턴스 멤버
    String instanceField;
    void instanceMethod() { }

    // 정적 멤버
    static String staticField;
    static void staticMethod() { }

    void localClassMethod() {
        //==로컬 클래스==//
        class LocalClass {
            void testMethod() {
                System.out.println(instanceField);
                instanceMethod();
                System.out.println(staticField);
                staticMethod();
            }
        }
    }
}

 

주의) 메소드의 매개 변수나 로컬 변수를 로컬 클래스에서 사용할 때

  • 로컬 클래스의 객체는 메소드 실행이 끝나도 힙 메모리에 존재해서 계속 사용될 수 있습니다.
  • 매개 변수나 로컬 변수는 메소드 실행이 끝나면 스택 메모리에서 사라지기 때문에 로컬 객체에서 사용할 경우 문제 발생

 

문제 해결 방법 (자바)

컴파일 시 로컬 클래스에서 사용하는 매개 변수나 로컬 변수의 값을 로컬 클래스 내부에 복사해 두고 사용

그리고 매개 변수나 로컬 변수가 수정되어 값이 변경되면 로컬 클래스에 복사해 둔 값과 달라지는 문제를 해결하기 위해 

매개 변수나 로컬 변수를 final로 선언해서 수정을 막습니다.

 

결론적으로 로컬 클래스에서 사용 가능한 것은 final로 선언된 매개 변수와 로컬 변수뿐

 

자바 7 이전

  • final 키워드 없이 선언된 매개 변수나 로컬 변수를 로컬 클래스에서 사용하면 컴파일 에러 발생
void localClassMethod(final int number) {
    final int ten = 10;

    //==로컬 클래스==//
    class LocalClass {
        int plus10() {
            return number + ten;
        }
    }
}

 

자바 8 이후

  • final 키워드 없이 선언된 매개 변수나 로컬 변수를 사용해도 컴파일 에러가 발생하지 않음
  • final 선언을 하지 않아도 여전히 값을 수정할 수 없는 final의 특성을 갖습니다.
  • final 키워드 존재 여부의 차이점은 로컬 클래스의 복사 위치

[final 키워드에 따른 복사 위치]

final 키워드가 있는 경우 로컬 클래스의 메소드 내부에 지역 변수로 복사

final 키워드가 없는 경우 로컬 클래스의 필드로 복사

 

final 키워드에 따른 복사 위치

 

void localClassMethod(int number) {
    int ten = 10;

    // number = 10; (x) final 특성
    // plusNumber = 100; (x) final 특성

    //==로컬 클래스==//
    class LocalClass {
        int plus10() {
            return number + ten;
        }
    }
}

 

3. 중첩 클래스에서 클래스 참조

  • 클래스 내부에서 this는 객체 자신의 참조
  • 중첩 클래스에서 this 키워드를 사용하면 바깥 클래스의 객체 참조가 아니라, 중첩 클래스의 객체 참조가 됩니다.
  • 중첩 클래스에서 바깥 클래스 참조를 얻으려면 바깥 클래스의 이름을 this 앞에 붙여주면 됩니다.
바깥클래스.this.필드;
바깥클래스.this.메소드();

 

예시

public class OuterPerson {
    public String name;

    public String getName() {
        return name;
    }

    //==인스턴스 멤버 클래스==//
    class NestedInstancePerson {
        private String name;

        public String getName() {
            return name;
        }

        public void printName() {
            OuterPerson.this.name = "OuterPerson"; // 바깥 클래스 필드
            this.name = "NestedInstancePerson"; // 자가 자신 필드(인스턴스 멤버 클래스 필드)

            System.out.println("Outer Person Name: " + OuterPerson.this.getName());
            System.out.println("Nested Instance PersonName: " + this.getName());
        }
    }

    // 실행
    public static void main(String[] args) {
        OuterPerson outerPerson = new OuterPerson();
        OuterPerson.NestedInstancePerson nestedInstancePerson = outerPerson.new NestedInstancePerson();
        nestedInstancePerson.printName();
    }
}