군만두의 IT 공부 일지

[스터디4] 05. 중첩 선언과 익명 객체 본문

프로그래밍/Java

[스터디4] 05. 중첩 선언과 익명 객체

mandus 2025. 2. 25. 19:29

목차

    9장. 중첩 선언과 익명 객체

    9.1 중첩 클래스

    • 중첩 클래스(Nested Class): 클래스 내부에 선언한 클래스
    • 장점: 클래스의 멤버를 쉽게 사용할 수 있고, 외부에는 중첩 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.
    • 중첩 클래스는 선언하는 위치에 따라 두 가지로 분류된다.
      • 멤버 클래스: 클래스의 멤버로서 선언되는 중첩 클래스
      • 로컬 클래스: 메소드 내부에서 선언되는 중첩 클래스
    선언 위치에 따른 분류 선언 위치 객체 생성 조건
    멤버 클래스 인스턴스 멤버 클래스 class A {
       class B { ... }
    }
    A 객체를 생성하야만
    B 객체를 생성할 수 있음
    정적 멤버 클래스 class A {
       static class B { ... }
    }
    A 객체를 생성하지 않아도
    B 객체를 생성할 수 있음
    로컬 클래스 class A {
       void method() {
          class B { ... }
       }
    }
    method가 실행할 때만
    B 객체를 생성할 수 있음

     

    중첩 클래스를 컴파일하면 바이트코드 파일(.class)이 별도로 생성된다. 바이트코드 파일의 이름은 다음과 같다.

    // 멤버 클래스
    A $ B .class
    // 로컬 클래스
    A $1 B .class

    9.2 인스턴스 멤버 클래스

    인스턴스 멤버 클래스는 A 클래스 멤버로 선언된 B 클래스를 말한다.

    [public] class A {
    	[public | private] class B {	// 인스턴스 멤버 클래스
        }
    }

    접근 제한자에 따른 인스턴스 멤버 클래스의 접근 범위

    구분 접근 범위
    public class B {} 다른 패키지에서 B 클래스를 사용할 수 있다.
    class {} 같은 패키지에서만 B 클래스를 사용할 수 있다.
    private class B {} A 클래스 내부에서만 B 클래스를 사용할 수 있다.
    • 인스턴스 멤버 클래스 B는 주로 A 클래스 내부에서만 사용한다.
    • B 객체는 A 클래스 내부 어디에서나 생성할 수는 없고, 인스턴스 필드값, 생성자, 인스턴스 메소드에서 생성할 수 있다. A 객체가 있어야 B 객체도 생성할 수 있다.

    B 객체를 A 클래스 외부에 생성하려면 default 또는 public 접근 제한을 가져야 하고, A 객체를 먼저 생성한 다음 B 객체를 생성해야 한다.

    A a = new A();	// A 객체 생성
    A.B b = a.new B();	// B 객체 생성

    인스턴스 멤버 클래스 B 내부에는 일반 클래스와 같이 필드, 생성자, 메소드 선언이 올 수 있다. Java 17부터 정적 필드정적 메소드 선언이 가능하다.

    // A.java
    package ch09.sec02.exam02;
    
    public class A {
        // 인스턴스 멤버 클래스
        class B {
            // 인스턴스 필드
            int field1 = 1;
    
            // 정적 필드 (Java 17부터 허용)
            static int field2 = 2;
    
            // 생성자
            B() {
                System.out.println("B-생성자 실행");
            }
    
            // 인스턴스 메소드
            void method1() {
                System.out.println("B-method1 실행");
            }
    
            // 정적 메소드 (Java 17부터 허용)
            static void method2() {
                System.out.println("B-method2 실행");
            }
        }
    
        // 인스턴스 메소드
        void useB() {
            // B 객체 생성 및 인스턴스 필드 및 메소드 사용
            B b = new B();
            System.out.println(b.field1);
            b.method1();
    
            // B 클래스의 정적 필드 및 메소드 사용
            System.out.println(B.field2);
            B.method2();
        }
    }

    9.3 정적 멤버 클래스

    정적 멤버 클래스static 키워드와 함께 A 클래스 멤버로 선언된 B 클래스를 말한다.

    [public] class A {
    	[public | private] static class B {	// 정적 멤버 클래스
        }
    }

    접근 제한자에 따른 정적 멤버 클래스의 접근 범위

    구분 접근 범위
    public static class B {} 다른 패키지에서 B 클래스를 사용할 수 있다.
    static class B {} 같은 패키지에서만 B 클래스를 사용할 수 있다.
    private static class B {} A 클래스 내부에서만 B 클래스를 사용할 수 있다.
    • 정적 멤버 클래스 B는 A 클래스 내외부에서 사용된다.
    • A 클래스 외부에서 A와 함께 많이 사용되기 때문에 default 또는 public 접근 제한을 가진다.
    • B 객체는 A 클래스 내부 어디든 객체를 생성할 수 있다.

    A 클래스 외부에서 B 객체를 생성하려면 A 객체 생성 없이 A 클래스로 접근해서 B 객체를 생성할 수 있다.

    A.B b = new A.B();

    정적 멤버 클래스 B 내부에는 일반 클래스와 같이 필드, 생성자, 메소드 선언이 올 수 있다.

    // A.java
    package ch09.sec03.exam02;
    
    public class A {
        // 정적 멤버 클래스
        static class B {
            // 인스턴스 필드
            int field1 = 1;
    
            // 정적 필드 (Java 17부터 허용)
            static int field2 = 2;
    
            // 생성자
            B() {
                System.out.println("B-생성자 실행");
            }
    
            // 인스턴스 메소드
            void method1() {
                System.out.println("B-method1 실행");
            }
    
            // 정적 메소드 (Java 17부터 허용)
            static void method2() {
                System.out.println("B-method2 실행");
            }
        }
    }

    9.4 로컬 클래스

    로컬(local) 클래스는 생성자 또는 메소드 내부에서 선언된 클래스를 말한다.

    [public] class A {
        // 생성자
        public A() {
        	class B {}
        }
        
        // 메소드
        public void method() {
        	class B {}
        }
    }
    • 로컬 클래스는 생성자와 메소드가 실행될 동안에만 객체를 생성할 수 있다.
    • 로컬 변수(생성자 or 메소드의 매개변수 or 내부에서 선언된 변수)를 로컬 클래스에서 사용할 경우, 변수는 final 특성을 갖게 되므로 값을 읽을 수만 있고 수정할 수 없다.
    • Java 8 이후부터는 명시적으로 final 키워드를 붙이지 않아도 되지만, 로컬 변수에 final 키워드를 추가해서 final 변수임을 명확히 할 수 있다.

    로컬 클래스 B 내부에는 일반 클래스와 같이 필드, 생성자, 메소드 선언이 올 수 있다. Java 17부터는 정적 필드와 정젝 메소드 선언이 가능하다.

    // A.java
    package ch09.sec04.exam02;
    
    public class A {
        // 메소드
        void useB() {
            // 로컬 클래스
            class B {
                // 인스턴스 필드
                int field1 = 1;
    
                // 정적 필드 (Java 17부터 허용)
                static int field2 = 2;
    
                // 생성자
                B() {
                    System.out.println("B-생성자 실행");
                }
    
                // 인스턴스 메소드
                void method1() {
                    System.out.println("B-method1 실행");
                }
    
                // 정적 메소드 (Java 17부터 허용)
                static void method2() {
                    System.out.println("B-method2 실행");
                }
            }
    
            // 로컬 객체 생성
            B b = new B();
    
            // 로컬 객체의 인스턴스 필드 및 메소드 사용
            System.out.println(b.field1);
            b.method1();
    
            // 로컬 클래스의 정적 필드 및 메소드 사용 (Java 17부터 허용)
            System.out.println(B.field2);
            B.method2();
        }
    }

    9.5 바깥 멤버 접근

    중첩 클래스는 바깥 클래스와 긴밀한 관계를 맺으면서 바깥 클래스의 멤버(필드, 메소드)에 접근할 수 있다. 하지만 중첩 클래스가 어떻게 선언되었느냐에 따라 접근 제한이 있을 수 있다.

    바깥 클래스의 멤버 접근 제한

    정적 멤버 클래스는 바깥 객체가 없어도 사용 가능해야 하므로 바깥 클래스의 인스턴스 필드와 인스턴스 메소드는 사용하지 못한다.

    구분 바깥 클래스의 사용 가능한 멤버
    인스턴스 멤버 클래스 바깥 클래스의 모든 필드와 메소드
    정적 멤버 클래스 바깥 클래스으 정적 필드와 정적 메소드
    // A.java
    package ch09.sec05.exam01;
    
    public class A {
        // A의 인스턴스 필드와 메소드
        int field1;
        void method1() { }
    
        // A의 정적 필드와 메소드
        static int field2;
        static void method2() { }
    
        // 인스턴스 멤버 클래스
        class B {
            void method() {
                // A의 인스턴스 필드와 메소드 사용
                field1 = 10;  // (O)
                method1();     // (O)
    
                // A의 정적 필드와 메소드 사용
                field2 = 10;   // (O)
                method2();     // (O)
            }
        }
    
        // 정적 멤버 클래스
        static class C {
            void method() {
                // A의 인스턴스 필드와 메소드 사용
                // field1 = 10;  // (X)
                // method1();     // (X)
    
                // A의 정적 필드와 메소드 사용
                field2 = 10;   // (O)
                method2();     // (O)
            }
        }
    }

    바깥 클래스의 객체 접근

    중첩 클래스 내부에서 this는 해당 중첩 클래스의 객체를 말한다. 중첩 클래스 내부에서 바깥 클래스의 객체를 얻으려면, 바깥 클래스 이름에 this를 붙인다.

    바깥클래스이름.this // 바깥객체
    // A.java
    package ch09.sec05.exam02;
    
    public class A {
        // A 인스턴스 필드
        String field = "A-field";
    
        // A 인스턴스 메소드
        void method() {
            System.out.println("A-method");
        }
    
        // 인스턴스 멤버 클래스
        class B {
            // B 인스턴스 필드
            String field = "B-field";
    
            // B 인스턴스 메소드
            void method() {
                System.out.println("B-method");
            }
    
            // B 인스턴스 메소드
            void print() {
                // B 객체의 필드와 메소드 사용
                System.out.println(this.field); // B의 field
                this.method(); // B의 method()
    
                // A 객체의 필드와 메소드 사용
                System.out.println(A.this.field); // A의 field
                A.this.method(); // A의 method()
            }
        }
    
        // A의 인스턴스 메소드
        void useB() {
            B b = new B();
            b.print();
        }
    }

    9.6 중첩 인터페이스

    중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다. 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해서이다.

    class A {
        [public | private | static] interface B {	// 중첩 인터페이스
            // 상수 필드
            // 추상 메소드
            // 디폴트 메소드
            // 정적 메소드
        }
    }

    중첩 인터페이스는 안드로이드와 같은 UI 프로그램에서 이벤트를 처리할 목적으로 많이 활용된다. 예를 들어, 버튼을 클릭했을 때 이벤트를 처리할 객체는 중첩 인터페이스를 구현해서 만든다.          

    9.7 익명 객체

    • 익명(anonymous) 객체: 이름이 없는 객체
    • 장점: 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성할 수 있다.
    • 익명 객체는 필드값, 로컬 변수값, 매개변수값으로 주로 사용된다.

    익명 객체는 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수 있다.

    • 익명 자식 객체: 클래스를 상속해서 만들 경우
    • 익명 구현 객체: 인터페이스를 구현해서 만들 경우

    익명 자식 객체

    익명 자식 객체는 부모 클래스를 상속받아 생성되고, 이렇게 생성된 객체는 부모 타입의 필드, 로컬 변수, 매개변수의 값으로 대입할 수 있다.

    new 부모생성자(매개값, ...) {
        // 필드
        // 메소드
    }

    중괄호 블록 안의 필드와 메소드는 익명 자식 객체가 가져야 할 멤버로, 중괄호 블록 안에서만 사용할 수 있다.

    익명 구현 객체

    익명 구현 객체는 인터페이스를 구현해서 생성되는데, 이렇게 생성된 객체는 인터페이스 타입의 필드, 로컬변수, 매개변수의 값으로 대입할 수 있다.

    new 인터페이스(매개값, ...) {
        // 필드
        // 메소드
    }

    중괄호 블록 안의 필드와 메소드는 익명 구현 객체가 가져야 할 멤버로, 중괄호 블록 안에서만 사용할 수 있다.

     

    이 글은 『이것이 자바다』 책을 학습한 내용을 정리한 것입니다.

    '프로그래밍 > Java' 카테고리의 다른 글

    [스터디4] 07. 멀티 스레드  (0) 2025.03.11
    [스터디4] 06. 제네릭  (0) 2025.03.02
    [스터디4] 04. 인터페이스  (1) 2025.02.16
    [스터디4] 03. 상속  (0) 2025.02.09
    [스터디4] 02. 클래스  (0) 2025.01.31
    Comments