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

Seunghee Lee·2023년 3월 6일
0

Java

목록 보기
5/9

1. 중첩 클래스와 중첩 인터페이스란 ?

  • 클래스가 여러 클래스와 관계를 맺는 경우에는 ▶ 독립적으로 선언하는 것이 좋음
  • 특정 클래스와 관계를 맺는 경우 ▶ 클래스 내부에 관계 클래스를 선언하는 것이 좋음

중첩 클래스(Nested Class)란 클래스 내부에 선언한 클래스를 말한다.

중첩 클래스 사용의 장점

- 중첩 클래스를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있다.
- 또한 외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.

✅ 코드 형태

class ClassName {
	class NestClassName {	// 중첩 클래스
    }
}

중첩 인터페이스(Nested Interface)란 클래스 내부에 선언한 인터페이스를 말한다.

중첩 인터페이스를 사용하는 이유

⇒ 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해
- 주로 UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용된다.

✅ 코드 형태

class ClassName {
	interface NestInterfaceName {	// 중첩 인터페이스
    }
}

ex) View 클래스의 클릭 이벤트를 처리하는 구현 클래스를 만들기

public class View {
	public interface OnClickListner {
    	public void onClick(View v);
    }
}

2. 중첩 클래스

중첩 클래스는 클래스 내부에 선언되는 위치에 따라 2가지로 분류된다.
1. 멤버 클래스
2. 로컬 클래스

멤버 클래스

멤버 클래스는 클래스의 멤버로서 선언되는 중첩 클래스

로컬 클래스

메소드 내부에서 선언되는 중첩 클래스

멤버 클래스와 로컬 클래스의 차이점

멤버 클래스는 클래스나 객체가 사용중이라면 언제든지 재사용이 가능하지만,
로컬 클래스는 메소드 실행 시에만 사용되고, 메소드가 실행 종료되면 없어진다.

1) 멤버 클래스 종류

인스턴스 멤버 클래스는 static 키워드 없이 선언된 클래스를 말한다.

  • 인스턴스 멤버 클래스는 인스턴스 필드와 메소드만 선언할 수 있다.

✅ 코드 형태

class A {
	/* 인스턴스 멤버 클래스*/
	class B {	
    	B() { }				// 생성자
        int field1;			// 인스턴스 필드
        void method1();		// 인스턴스 메서드
    }
}

정적 멤버 클래스는 static 키워드로 선언된 클래스를 말한다.

  • 정적 멤버 클래스는 모든 종류의 필드와 메소드를 선언할 수 있다.

✅ 코드 형태

class A {
	/* 정적 멤버 클래스*/
	static class B {	
    	B() { }					// 생성자
        int field1;				// 인스턴스 필드
        void method1();			// 인스턴스 메서드
        static int field2		// 정적 필드 
        static void method2()	// 정적 메서드
    }
}
  • A클래스 외부에서 정적 멤버 클래스 C의 객체를 생성하기 위해서는 A객체를 생성할 필요 없이 다음과 같이 생성한다.
A.C c = new A.C();	
c.field1 = 3;		// 인스턴스 필드 사용
c.method1();		// 인스턴스 메소드 호출
A.C.field2 = 3;		// 정적 필드 사용
A.C.method2();		// 정적 메소드 호출

2) 로컬 클래스

  • 로컬 클래스는 접근 제한자(public, private) 및 static을 붙일 수 없다.
    - 오직 메소드 내부에서만 사용되므로 접근을 제한할 필요가 없기 때문이다.

  • 로컬 클래스 내부에는 인스턴스 필드와 메소드만 선언이 가능하다.

✅ 코드 형태

void method() {
	/* 로컬 클래스*/
	class D {	
    	D() { }					// 생성자
        int field1;				// 인스턴스 필드
        void method1();			// 인스턴스 메서드
   	}
    D d = new D();
    d.field1 = 3;
    d.method1();
}
  • 로컬 클래스는 메소드가 실행될 때 메소드 내에서 객체를 생성하고 사용해야 한다.
    - 주로 비동기 처리를 위해 스레드 객체를 만들 때 사용한다.

멤버 클래스와 로컬 클래스를 종합해서 사용하면 다음과 같다.

  • [ A.java ] - 중첩 클래스
public class A {
    A() {
        System.out.println("A 객체 생성됨");
    }

    class B {
        B() {
            System.out.println("B 객체 생성됨");
        };
        int field1;
        void method1() {}
    }

    static class C {
        C() {
            System.out.println("C 객체 생성");
        }
        int field1;
        static int field2;
        void method1() {}
        static void method2() {}
    }

    void method() {
        class D {
            D() {
                System.out.println("D 객체 생성");
            }
            int field1;
            void method1() {}
        }
        D d = new D();
        d.field1 = 3;
        d.method1();
    }
}
  • [ Main.java ] - 중첩 클래스 객체 생성
public class Main {
    public static void main(String... args) {
        A a = new A();

        A.B b = a.new B();
        b.field1 = 3;
        b.method1();

        A.C c = new A.C();
        c.field1 = 3;
        c.method1();
        A.C.field2 = 3;
        A.C.method2();

        a.method();

    }
}


3. 중첩 클래스의 접근 제한

멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라 접근 제한이 생긴다.

(1) 바깥 클래스의 필드와 메소드에 사용 제한이 생긴다.

  • 인스턴스 멤버 클래스는
    - 바깥 클래스의 인스턴스 필드의 초기값이나 메소드에서 객체를 생성할 수 있지만,
    - 정적 필드의 초기값이나 정적 메소드에서는 객체를 생성할 수 없다.
  • 정적 멤버 클래스는
    - 모든 필드의 초기값이나 모든 메소드에서 객체를 생성할 수 있다.

(2) 멤버 클래스 내부에서 바깥 클래스의 필드와 메소드를 접근할 때에도 제한이 생긴다.

  • 인스턴스 멤버 클래스 안에서는 바깥 클래스의 모든 필드와 메소드에 접근할 수 있지만,
  • 정적 멤버 클래스 안에서는 바깥 클래스의 정적 필드와 메소드에만 접근할 수 있다.

(3) 로컬 클래스 내부에서 바깥 클래스의 필드나 메소드를 제한없이 사용할 수 있지만, 메소드의 매개변수나 로컬 변수는 메소드 실행이 끝나면 스택 메모리에서 사라진다.

  • 자바8 이후부터 final 키워드 없이 선언된 매개변수와 로컬 변수를 사용해도 컴파일 에러가 발생하지 않는다 !

  • 그렇다면 final 키워드 존재 여부의 차이점은 ? ▶ 로컬 클래스의 복사 위치 !
    - final키워드가 있다면 → 로컬 클래스의 메소드 내부에 지역변수로 복사된다
    - fianl키워드가 없다면 → 로컬 클래스의 필드로 복사된다.


중첩 클래스에서 바깥 클래스 접근하기 위해선 this카워드를 사용한다.

  • 중첩 클래스에서 this키워드를 사용하면 ▶ 중첩 클래스의 객체 참조가 된다.
    - 중첩 클래스 내부에서 this.필드, this.메소드()로 호출하면 중첩 클래스의 필드와 메소드가 사용된다.
    - 중첩 클래스 내부에서 바깥 클래스의 객체 참조를 얻으려면 ▶ 바깥클래스.this
바깥클래스.this.필드
바깥클래스.this.메소드();

ex) 중첩 클래스에서 바깥 클래스 참조 얻기

  • [ Outter.java] - 참조 얻는 클래스
public class Outter {
    String field = "Outter-field";
    void method() {
        System.out.println("Outter-field");
    }

    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.java ] - 실행 클래스
public class OutterExample {
    public static void main(String... args) {
        Outter outter = new Outter();
        Outter.Nested nested = outter.new Nested();
        nested.print();
    }
}


4. 중첩 인터페이스

앞서 말했듯이, 중첩 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위함이다.
그래서 주로 UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용된다.

ex) Button을 클릭했을 때 이벤트를 처리하는 객체를 받고 싶다. 단, Button 내부에 선언된 중첩 인터페이스를 구현한 객체만 받아야 할 때, 다음과 같이 선언한다.

  • [ Button.java] - 중첩 인터페이스
public class Button {
    OnClickListener listener;

    void setOnClickListener(OnClickListener listener) {
        this.listener = listener;
    }

    void touch() {
        listener.onClick();
    }

    interface OnClickListener {
        void onClick();
    }
}
  • [ CallListener.java] - 전화 구현 클래스
public class CallListener implements Button.OnClickListener{
    @Override
    public void onClick() {
        System.out.println("calling");
    }
}
  • [ MessageListener.java] - 메시지 구현 클래스
public class MessageListener implements Button.OnClickListener{
    @Override
    public void onClick() {
        System.out.println("message");
    }
}
  • [ ButtonExample.java] - 버튼 이벤트 처리; 실행 클래스
public class ButtonExample {
    public static void main (String... args) {
        Button btn = new Button();

        btn.setOnClickListener(new CallListener());
        btn.touch();

        btn.setOnClickListener(new MessageListener());
        btn.touch();
    }
}


5. 익명 객체

익명(Anonymous)객체는 이름이 없는 객체를 말한다.

  • 익명 객체는 단독으로 생성할 수 없고 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수있다.
  • 익명 객체는 필드의 초기값이나 로컬 변수의 초기값, 매개 변수의 매개값으로 주로 대입된다.
  • UI 이벤트 처리 객체나 스레드 객체를 간편하게 생성할 목적으로 많이 활용된다.
profile
자라나라 개발개발 ~..₩

0개의 댓글