혼자 공부하는 자바 - 9장. 중첩 클래스와 중첩 인터페이스

youngtae·2023년 3월 30일
0

자바 이론

목록 보기
8/12
post-thumbnail

중첩 클래스와 중첩 인터페이스 소개

  • 중첩 클래스 : 클래스 내부에서 선언한 클래스
  • 중첩 클래스를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있고, 외부에는 불필요한 관계 클래스를 감추어서 코드의 복잡성을 줄일 수 있는 장점이 있다.
class ClassName {
	class NestedClassName { // 중첩클래스
	}
}
// 인터페이스도 클래스 내부에 선언 가능
class ClassName {
	interface NestedInterfaceName {  // 중첩 인터페이스
	}
}

중첩 클래스

  • 클래스 내부에 선언되는 위치에 따라 두가지로 분류
  • 클래스 멤버로서 선언되는 중첩 클래스 - 멤버 클래스
    • 클래스나 객체가 사용 중이라면 언제든지 재사용이 가능
  • 생성자나 메소드 내부에서 선언되는 중첩 클래스 - 로컬클래스
    • 메소드 실행할 때만 사용되고 종료되면 사라짐

  • 중첩클래스도 클래스이기 때문에 컴파일하면 .class 파일이 별도로 생성

    A $ B.class A: 바깥 클래스, B: 멤버 클래스
    A $1 B.class A: 바깥 클래스, B: 로컬 클래스

  • 인스턴스 멤버 클래스

    • static 키워드 없이 중첩 선언된 클래스
    • 인스턴스 필드와 메소드만 선언 가능
    /**바깥 클래스**/
    class A {
    	A() { System.out.println("A 객체가 생성됨"); }
    	
    	/**인스턴스 멤버 클래스**/
    	public class B {
    		// 생성자
    		B() { System.out.println("B 객체가 생성됨"); }
    		int field1;  // 인스턴스 필드
    		//static int field2;  // 정적필드 사용 불가
    		void method1() { }  // 인스턴스 메소드
    		//static void method2() { };  // 정적메소드 사용 불가
    
    	}
  • 정적 멤버 클래스

    • static 키워드로 선언된 클래스
    • 모든 종류의 필드와 메소드 선언 가능
    class A {
        /**정적 멤버 클래스**/
            static class C {
                C() { System.out.println("C 객체가 생성됨"); }
                int field1;
                static int field2;
                void method1() { }
                static void method2() { }
            }
    • A 클래스 외부에서 정적 멤버 클래스 C의 객체 생성할 경우
      A.C c = new A.C();
      c.field1 = 3;  // 인스턴스
      c.method1();
      A.C.field2 = 3;  // 정적
      A.C.method2();
  • 로컬 클래스

    • 접근 제한자, static 불일 수 없음 → 메소드 내부에서만 사용되므로 접근 제한할 필요 없기 때문
      void method() {
      		/**로컬 클래스**/
      		class D {
      			D() { System.out.println("D 객체가 생성됨"); }
      			int field1;
      			//static int field2;
      			void method1() { }
      			//static void method2() { }
      		}
      		D d = new D();
      		d.field1 = 3;
      		d.method1();
      	}
    • 주로 비동기 처리를 위해 스레드 객체를 만들 때 사용
      void method() {
      	class DownloadThread extends Thread { ...}
      	DownloadThread thread = new DownloadThread();
      	thread.start();
      }
  • 중첩 클래스 객체 생성
    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();
    	}
    }
    // 출력결과
    A 객체가 생성됨
    B 객체가 생성됨
    C 객체가 생성됨
    D 객체가 생성됨
    인스턴스 : 바깥.new 멤버(), 정적: new 바깥.멤버() !!

중첩 클래스의 접근 제한

  • 바깥 필드와 메소드에서 사용제한
    • 바깥 클래스에서 인스턴스 멤버 클래스 사용할 때 제한

      public class A {
      	//인스턴스 필드
      	B field1 = new B();               
      	C field2 = new C();               
      	
      	//인스턴스 메소드
      	void method1() {
      		B var1 = new B();
      		C var2 = new C();
      	}
      	
      	//정적 필드 초기화
      	//static B field3 = new B();
      	static C field4 = new C();
      	
      	//정적 메소드
      	static void method2() {
      		//B var1 = new B();
      		C var2 = new C();
      	}
      	
      	//인스턴스 멤버 클래스
      	class B {}
      	//정적 멤버 클래스
      	static class C {}	
      }
    • 위의 코드에서 인스턴스 멤버 클래스 B는 바깥 클래스의 인스턴스 필드 field1의 초기값이나
      인스턴스 메소드 method1에서 객체 생성할 수 있지만
      정적 필드 field3의 초기값이나 정적 메소드 method2에서는 객체를 생성할 수 없다.

    • 하지만 정적 멤버 클래스 C는 모든 필드의 초기값이나 모든 메소드에서 객체 생성할 수 있다.

  • 멤버 클래스에서 사용 제한
    • 멤버 클래스 내부에서 바깥 클래스의 필드와 메소드에 접근할 때 제한

      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();
      		}
      	}	
      }
  • 로컬 클래스에서 사용 제한
    • 로컬 클래스의 객체는 메소드 종료되면 없어지는 것이 일반적이지만, 로컬 스레드 객체를 사용할 때는 종료되어도 계속 실행 상태로 존재

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

      public void method2(int arg) {
      		int localVariable = 1;
      		//arg = 100; (x)
      		//localVariable = 100; (x)
      		class Inner {
      			public void method() {
      				int result = arg + localVariable;
      			}
      		}
      	}
  • 중첩 클래스에서 바깥 클래스 참조 얻기
    • 중첩 클래스에서 this 키워드 사용하면 중첩 클래스의 객체 참조함

    • 바깥 클래스의 객체 탐조 얻으려면 바깥 클래스.this 로 사용

      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-method");
      		}
      		void print() {
      			System.out.println(this.field);  // 중첩 객체 참조
      			this.method();
      			System.out.println(Outter.this.field);  // 바깥 객체 참조
      			Outter.this.method();
      		}
      	}
      }

중첩 인터페이스

  • 클래스의 멤버로 선언된 인터페이스
  • 인스턴스 멤버 인터페이스는 바깥 클래스의 객체가 있어야 사용 가능
  • 정적 멤버 인터페이스는 바깥 클래스의 객체 없이 바깥 클래스만으로 바로 접근 가능
public class Button {
	OnClickListener listener;  //인터페이스 타입 필드
	
	void setOnClickListener(OnClickListener listener) {  //매개변수의 다형성
		this.listener = listener;
	}
	
	void touch() {
		listener.onClick(); // 구현객체의 onClick() 메소드 호출
	}
	
	static interface OnClickListener {  //중첩 인터페이스
		void onClick();
	}
}

익명 객체

  • 익명 객체 : 이름 없는 객체
  • 만들려면 어떤 클래스를 상속하거나 인터페이스를 구현해야함

익명 자식 객체 생성

  • 일반적인 경우
class Child extends Parent {   }

class A {
	Parent field = new Child();
	void method() {
		Parent localVar = new Child();
	}
}
  • 자식 클래스가 재사용되지 않고 특정 위치에서만 사용할 경우 익명 객체 사용이 효과적
// 필드 선언할 때 초기값으로 익명 자식 객체 생성해서 대입
class A {
	Parent field = new Parent() {
		int childField;
	voidchildMethod() {  }
	@Override
	void parentMethod() {  }
	};  // 하나의 실행문이므로 끝에는 세미콜론 반드시 붙이기
}

익명 구현 객체 생성

  • new 연산자 이용해서 구현 객체 생성 후 인터페이스 타입의 필드 또는 로컬 변수 대입이 일반적
class TV implements RemoteControl {  }

class A {
	RemoteControl field = new TV();
	void method() {
		RemoteControl localVar = new TV();
	}
}
  • 구현 클래스가 재사용되지 않고 특정 위치에서만 사용할 경우 익명 구현 객체 사용이 효과적
class A {
	RemoteControl field = new RemoteControl() {
		@Override
		void turnOn() { }
	};
}
profile
나의 개발기록

0개의 댓글