[Java] 중첩 클래스와 중첩 인터페이스 ②

kiteB·2022년 1월 19일
0

Java

목록 보기
23/35
post-thumbnail

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

1. 바깥 필드와 메소드에서 사용 제한

멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라 바깥 클래스의 필드와 메소드에 사용 제한이 생긴다.

  • 인스턴스 멤버 클래스 B
    • 바깥 클래스의 인스턴스 필드(field1)의 초기값이나 인스턴스 메소드(method1())에서 객체를 생성할 수 있으나,
    • 정적 필드(field3)의 초기값이나 정적 메소드(method2())에서는 객체를 생성할 수 없다.
  • 반면 정적 멤버 클래스 C모든 필드의 초기값이나 모든 메소드에서 객체를 생성할 수 있다.
public class A {
    //인스턴스 필드
    B field1 = new B();     //(o)
    C field2 = new C();     //(o)

    //인스턴스 메소드
    void method1() {
        B var1 = new B();   //(o)
        C var2 = new C();   //(o)
    }

    //정적 필드 초기화
    //static B field3 = new B();    //(x)
    static C field4 = new C();      //(o)

    //정적 메소드
    static void method2() {
        //B var1 = new B();  //(x)
        C var2 = new C();   //(o)
    }

    //인스턴스 멤버 클래스
    class B { }
    
    //정적 멤버 클래스
    static class C { }
}

2. 멤버 클래스에서 사용 제한

멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라
멤버 클래스 내부에서 바깥 클래스의 필드와 메소드를 접근할 때에도 제한이 따른다.

  • 인스턴스 멤버 클래스 B 안에서는 바깥 클래스의 모든 필드의 초기값이나 모든 메소드에서 접근할 수 있다.
  • 반면 정적 멤버 클래스 C 안에서는
    • 바깥 클래스의 정적 필드(field2)와 메소드(method2())에만 접근할 수 있고,
    • 인스턴스 필드(field1)나 메소드(method1())는 접근할 수 없다.
public class A {
    int field1;
    void method1() { }
    
    static int field2;
    static void method2() { }
    
    class B {
        void method() {
            field1 = 10;
            method1();
            
            field2 = 10;
            method2();
        }
    }
    
    static class C {
        void method() {
            //field1 = 10;  //인스턴스 필드에 접근할 수 없다.
            //method1();    //인스턴스 메소드에 접근할 수 없다.
            
            field2 = 10;
            method2();
        }
    }
}


3. 로컬 클래스에서 사용 제한

로컬 클래스 내부에서는 바깥 클래스의 필드나 메소드를 제한 없이 사용할 수 있다.

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

자바에서는 이러한 문제를 해결하기 위해 컴파일 시 로컬 클래스에서 사용하는 매개 변수나 로컬 변수의 값을 로컬 클래스 내부에 복사해 두고 사용한다. 그리고 매개 변수나 로컬 변수가 수정되어 값이 변경되면 로컬 클래스에 복사해 둔 값과 달라지는 문제를 해결하기 위해 매개 변수나 로컬 변수를 final로 선언해서 수정을 막는다.

💡 즉, 로컬 클래스에서 사용 가능한 것은 final로 선언된 매개 변수와 로컬 변수뿐!

자바 7 이전까지는 final 키워드 없이 선언된 매개 변수와 로컬 변수를 로컬 클래스에서 사용하면 컴파일 에러가 발생했지만, 자바 8 부터는 컴파일 에러가 나지 않는다. 그렇다고 final이 아닌 매개 변수와 로컬 변수를 허용한다는 것은 아니다! final 선언을 하지 않아도 여전히 값을 수정할 수 없는 final 특성을 갖는다.

final 키워드 존재 여부의 차이점은 로컬 클래스의 복사 위치이다.

  • final 키워드가 있다면 로컬 클래스의 메소드 내부에 지역 변수로 복사되지만,
  • final 키워드가 없다면 로컬 클래스의 필드로 복사된다.

4. 중첩 클래스에서 바깥 클래스 참조 얻기

클래스 내부에서 this는 객체 자신의 참조이다. 중첩 클래스에서 this 키워드를 사용하면 바깥 클래스의 객체 참조가 아니라, 중첩 클래스의 객체 참조가 된다.

중첩 클래스 내부에서 바깥 클래스의 객체 참조를 얻으려면 바깥 클래스의 이름this 앞에 붙여주면 된다.

바깥클래스.this.필드
바깥클래스.this.메소드();

예제

  • Outter - 중첩 클래스에서 바깥 클래스 참조 얻기
public class Outter {
    String field = "Outter-field";

    void method() {
        System.out.println("Outter-method");
    }

    class Nested {
        String field = "Nested-field";

        void method() {
            System.out.println("Nested-field");
        }

        void print() {
            // 중첩 객체 참조
            System.out.println(this.field);
            this.method();

            // 바깥 객체 참조
            System.out.println(Outter.this.field);
            Outter.this.method();
        }
    }

}
  • OutterExample - 실행 클래스
public class OutterExample {
    public static void main(String[] args) {
        Outter outter = new Outter();
        Outter.Nested nested = outter.new Nested();
        nested.print();
    }
}
  • 실행 결과
Nested-field
Nested-method
Outter-field
Outter-method

[ 참고자료 ]

이것이 자바다 책

profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글