티스토리 뷰

9.1 중첩 클래스

  • 중첩 클래스: 클래스 내부에 선언한 클래스
  • 멤버 클래스: 클래스의 멤버로서 선언되는 중첩 클래스
  • 로컬 클래스: 메소드 내부 선언되는 중첩 클래스
  • 특정 클래스만 관계를 맺을 경우에는 중첩 클래스로 선언하는 것이 유지보수에 도움이 된다.
  • 클래스의 멤버를 쉽게 사용할 수 있음
  • 코드의 복잡성을 줄일 수 있음
  • 컴파일하면 바이트코드 파일이 생성

9.2 인스턴스 멤버 클래스

  • 바깥 클래스에서만 사용되기 때문에 내부 클래스는 주로 private접근 제한을 갖는 것이 일반적
  • 바깥 클래스 어디에서나 생성할 수는 없고 인스턴스 필드값, 생성자, 인스턴스 메소드에서 생성할 수 있음
  • 왜냐면 바깥 클래스 객체가 있어야 내부 클래스 객체도 생성할 수 있기 때문이다
  • 내부 클래스를 바깥 클래스 외부에 생성하기 위해서는 바깥 클래스를 먼저 생성한 다음 내부 클래스의 객체를 생성해야 한다.
public class InstanceClassDemo {
    public static void main(String[] args) {
        InsideClass insideClass = new InsideClass();
        
        // 바깥 클래스 객체 생성 후 내부 클래스 객체 생성
        InsideClass.OusideClass ousideClass = insideClass.new OusideClass();
    }
}

class InsideClass {
    // 내부 클래스
    class OusideClass {
        int field = 1; // 필드
        OusideClass() {} // 생성자
        void method1() {} // 메소드
        // Java 17부터는 정적 필드, 정적 멧드 사용 가능
    }
    
    // 인스턴스 필드값으로 생성
    OusideClass ousideClass = new OusideClass();
    
    // 생성자 안에서 생성
    InsideClass(){
        OusideClass ousideClass = new OusideClass();
    }
    
    // 메소드 안에서 생성
    void method(){
        OusideClass ousideClass = new OusideClass();
    }
}

9.3 정적 멤버 클래스

  • 내부 클래스는 바깥 클래스 외부에서도 사용되기 때문에 public, (default) 접근 제한을 갖는 것이 일반적
  • 내부 클래스는 바깥 클래스 어디에서든 생성할 수 있음
  • 내부 클래스를 외부에서 사용하기 위해서 바깥 클래스 객체는 생성하지 않아도 된다.
public class StaticClassDemo {
    public static void main(String[] args) {
        // 바깥 클래스 객체 없이 바로 접근해서 생성 가능
        InsideClass2.OusideClass2 ousideClass2 = new InsideClass2.OusideClass2();
        InsideClass2.OusideClass2.field = 2;
    }
}

class InsideClass2 {
    // 정적 멤버 클래스
    static class OusideClass2 {
        static int field = 1; // 필드
        OusideClass2() {} // 생성자
        void method1() {} // 메소드
        // Java 17부터는 정적 필드, 정적 멧드 사용 가능
    }

    // 인스턴스 필드값으로 생성
    OusideClass2 ousideClass = new OusideClass2();
    
    // 정적 필드값으로 생성
    static OusideClass2 outsideClass2 = new OusideClass2();

    // 생성자 안에서 생성
    InsideClass2(){
        OusideClass2 ousideClass = new OusideClass2();
    }

    // 메소드 안에서 생성
    void method() {
        OusideClass2 ousideClass = new OusideClass2();
    }

    // 정적 메소드 안에서 생성
    static void method2() {
        OusideClass2 ousideClass = new OusideClass2();
    }
}

 

9.4 로컬 클래스

  • 생성자 또는 메소드 내부에 생성된 클래스
  • 생성자와 메소드가 실행될 동안에만 객체를 생성할 수 있음
  • 로컬 변수를 로컬 클래스에서 사용할 경우, 로컬 변수는 final 특성을 갖게 되어 읽을 수만 있고 수정은 불가
class A {
    A() { 
        class B { 
            int field = 0;
            B() {}
            void bMethod() { }
        }
        
        B b = new B();
        b.bMethod();
    }
    
    void method() {
        int var = 1;
        
        class C {
           // var = 2; 변경 불가
        }
    }
}

 

9.5 바깥 멤버 접근

내부 클래스는 바깥 클래스의 멤버에 접근할 수 있음

  • 인스턴스 멤버 클래스는 바깥 클래스의 인스턴스 필드와 메소드, 정적 필드와 메소드 모두 사용 가능
  • 정적 멤버 클래스는 바깥 객체가 없어도 사용 가능해야 함로 바깥 클래스의 정적 필드와 메소드만 사용 가능

바깥 클래스의 객체 접근

public class ADemo {
    public static void main(String[] args) {
        A2 a2 = new A2();
        a2.useB2();
    }
}

class A2 {
    String field = "A2";

    class B2 {
        String field = "B2";

        void print() {
            // 내부 클래스의 필드 사용할 때
            System.out.println(this.field);

            // 바깥 클래스의 필드 사용할 때
            System.out.println(A2.this.field);
        }
    }

    void useB2() {
        B2 b2 = new B2();
        b2.print();
    }
}

 

9.6 중첩 인터페이스

  • 클래스의 멤버로 선언된 인터페이스
  • 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해서
  • 중첩 인터페이스는 암시적으로 static
  • 안드로이드와 같은 UI프로그램에서 이벤트를 처리할 목적으로 많이 활용됨
public class ButtonDemo {
    public static void main(String[] args) {
        Button okbutton = new Button();

        // Ok버튼이 클릭되었을 때 실행될 구현부를 작성
        class OKListener implements Button.ClickListener {
            @Override
            public void onClikc() {
                System.out.println("OK버튼 클릭");
            }
        }

        // 구현 객체 주입
        okbutton.setClickListener(new OKListener());

        // 버튼 누르기
        okbutton.press();
    }
}

class Button {
    interface ClickListener {
        void onClikc();
    }

    // 외부에서 settter를 통해 ClickListener 구현 객체를 필드에 저장할 수 있도록 함
    private ClickListener clickListener;

    public void setClickListener(ClickListener clickListener) {
        this.clickListener = clickListener;
    }

    // button이 클릭되어쓸 때 실행되는 메소드 선언
    public void press(){
        clickListener.onClikc();
    }
}

 

9.7 익명 객체

  • 이름이 없는 객체
  • 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성할 수 있음
  • 필드값, 로컬 변수값, 매개변수값으로 주로 사요

익명 자식 객체 = 클래스를 상속해서 만들 경우

public class CarDemo {
    public static void main(String[] args) {
        Car car = new Car();

        car.run1();
        car.run2();
        car.run3();

        car.run4(new Tire(){
            @Override
            void roll() {
                System.out.println("익명 자식 객체 매개값으로 타이어가 굴러갑니다.");
            }
        });
    }
}

class Car {
    Tire tire1 = new Tire();

    void run1() {
        tire1.roll();
    }

    // 필드값으로 사용
    Tire tire2 = new Tire() {
        @Override
        void roll() {
            System.out.println("익명 자식 객체 필드값 타이어가 굴러갑니다");
        }
    };

    void run2() {
        tire2.roll();
    }

    // 로컬 변수값으로 사용
    void run3() {
        Tire tire = new Tire() {
            @Override
            void roll() {
                System.out.println("익명 자식 객체 로컬 변수 값 타이어가 굴러갑니다");
            }
        };

        tire.roll();
    }

    // 매개변수값으로 사용
    void run4(Tire tire) {
        tire.roll();
    }
}

class Tire {
    void roll(){
        System.out.println("타이어가 굴러갑니다");
    }
}

 

익명 구현 객체 = 인터페이스를 구현해서 만들 경우

(익명 자식 객체 참고, 클래스가 인터페이스가 되었을 뿐 흡사함)

최근에 올라온 글