Java 파트9-1. 중첩 클래스와 중첩 인터페이스

1. 시작하기 전에


클래스는 긴밀한 관계를 가지는 클래스와 인터페이스를 내부에 선언할 수 있다.

  • 중첩 클래스
    • 클래스 내부에 선언한 클래스
    • 바깥 클래스와 긴밀한 관계를 가짐
  • 중첩 인터페이스
    • 클래스 내부에 선언한 인터페이스
    • 바깥 클래스와 긴밀한 관계를 가짐


2. 중첩 클래스


그림1

  • 멤버 클래스 : 클래스의 멤버로서 선언되는 중첩 클래스
    • 인스턴스 멤버 클래스 : 바깥 클래스의 객체가 있어야 사용 가능
    • 정적 멤버 클래스 : static과 함께 사용하고 바깥 클래스의 객체가 없어도 사용 가능
  • 로컬 클래스 : 생성자 또는 메소드 내부에서 선언되는 중첩 클래스, 생성자 또는 메소드가 실행할 때만 사용

인스턴스 멤버 클래스

  • 바깥 클래스의 객체가 있어야만 사용 가능하다.
  • 인스턴스 필드와 메소드만 선언 가능하고 정적 필드와 메소드는 선언할 수 없다.
  • 바깥 클래스 외부에서 인스턴스 멤버 클래스를 사용할 때는 ‘바깥클래스.인스턴스멤버클래스’ 로 사용한다.
    • ex) A.B 변수명
  • 객체 생성 시 바깥 클래스의 변수.new 인스턴스멤버클래스 생성자
    • ex) a.new B()

그림2

만약 A클래스 내부에서 methodA 앞에 static이 붙어 있다면 문제가 생긴다. methodA의 실행문에는 class B를 사용하고 있다. 인스턴스 멤버 클래스의 사용 조건은 반드시 바깥 클래스의 객체가 생성되어 있어야 한다. 그런데 static이 붙게 되면 methodA는 class A의 객체 생성 없이도 사용할 수 있게 된다. 따라서 모순이 발생하게 된다. 즉, 인스턴스 멤버 클래스를 바깥 클래스의 메소드의 실행문 내에서 사용하려면 메소드에 static을 붙이면 안된다.

정적 멤버 클래스

  • static 키워드로 선언된 클래스
  • 생성자, 인스턴스 필드, 정적 필드, 인스턴스 메소드, 정적 메소드 모두 사용 가능
  • 바깥 클래스의 객체 없이 사용 가능

그림3

// 객체 생성
// 인스턴스 멤버 클래스와 달리 바깥 클래스의 객체 생성 없이 사용 가능
바깥클래스.정적멤버클래스 변수명 = new 바깥클래스.정적클래스생정자 ()
A.C c = new A.C();

// static이므로 클래스만으로도 접근이 가능
A.C.method2(); 

로컬 클래스

  • 생성자 또는 메소드 내에서 선언된 클래스
  • 접근 제한자 및 static 붙일 수 없음
  • 인스턴스 필드와 메소드만 선언할 수 있고 정적 필드와 메소드는 선언 불가
  • 로컬 클래스 선언 후 사용하는 곳은 생성자 또는 메소드 내에서 사용한다.
    • 즉, 생성자 또는 메소드에서 선언하고 그 안에서만 사용한다고 이해하면 된다.

그림4


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


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

그림5

클래스 A에 인스턴스 멤버 클래스 B와 정적 멤버 클래스 C가 선언되었다고 하자.

  • 클래스 A의 인스턴스 필드와 인스턴스 메소드로 B와 C를 모두 사용 가능하다.
    • C는 정적 멤버 클래스이므로 A의 객체 생성 여부와 관계없이 사용 가능
    • 클래스 A는 정적이 아니므로 객체를 만들어 사용해야 한다. 따라서 인스턴스 멤버 클래스 B의 사용조건이 맞춰지므로 B도 사용 가능
  • 클래스 A의 정적 필드로는 C만 사용 가능하다.
    • 정적 필드는 A의 객체 없이 사용이 가능한 것인데 B는 사용 조건으로 A객체가 필요하기 때문에 사용 불가
  • 클래싀 A의 정적 메소드 내에서는 C만 사용 가능하다.
    • 정적 메소드는 A의 객체 없이 사용 가능한 것인데 B는 사용 조건으로 A객체가 필요하기 때문에 사용 불가

멤버 클래스에서 사용 제한

그림6

인스턴스 멤버 클래스 B는 static이 없으므로 사용하기 위해서는 A클래스의 객체가 필요하다. 결론적으로 클래스 B의 메소드를 호출하려면 A클래스의 객체를 우선적으로 만들어야 한다. 따라서 A클래스의 객체가 만들어진 상태이기 때문에 클래스 B에서는 클래스 A의 (정적, 인스턴스) 필드와 메소드를 사용할 수 있다.

그림7

정적 멤버 클래스의 C의 경우는 클래스 A의 객체가 없어도 사용할 수 있다. 결론적으로 클래스 C의 메소드를 호출하는데 클래스 A의 객체가 없는 것이므로 클래스 A의 인스턴스 필드와 메소드는 사용할 수 없다. 하지만 클래스 A의 정적 필드와 정적 메소드는 클래스 A의 객체가 없어도 사용 가능하기 때문에 클래스 A의 정적 필드와 정적 메소드는 사용할 수 있다.

로컬 클래스에서 사용 제한

매개 변수나 로컬 변수를 로컬 클래스에서 사용할 경우 매개 변수나 로컬 변수는 final 특성을 가지고 있다.

그림8

로컬은 메소드내, 생성자내에 선언된 것들을 의미한다고 보면 된다. 메소드와 생성자의 매개 인자, 로컬 변수는 final 특성을 가지므로 절대 수정이 불가능하다는 것을 기억하자.

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

중첩 클래스도 하나의 클래스이기 때문에 내부에서 this를 사용할 수 있다. this 키워드는 현재 자신의 객체를 의미한다. 따라서 중첩 클래스 안에서 this를 사용하면 this는 중첩 클래스로부터 생성된 객체를 의미한다.
중첩 클래스에서 바깥 클래스를 참조하고 싶다면 바깥 클래스의 이름을 this앞에 붙이면 된다.

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

// 예시
// 클래스 파일
package sec01.exam01;

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() {
			// Nested 클래스의 field와 method를 의미
			System.out.println(this.field);
			this.method();
			
			// Outter 클래스의 필드와 메소드를 의미
			System.out.println(Outter.this.field);
			Outter.this.method();
		}
	}
}

// 메인 실행 클래스 파일
package sec01.exam01;

public class OutterExam {

	public static void main(String[] args) {
		Outter outter = new Outter();
		Outter.Nested nested = outter.new Nested();
		nested.print();
		

	}
}


4. 중첩 인터페이스


  • 클래스의 멤버로 선언된 인터페이스
  • 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위함
  • 바깥 클래스가 없다면 의미가 없는 인터페이스의 경우 중첩 인터페이스로 사용함
  • 인스턴스 멤버 인터페이스와 정적 멤버 인터페이스 모두 가능함

UI프로그램 안드로이드 등 UI요소 내부에 선언된 중첩 인터페이스를 가지고 이벤트 처리를 많이 하므로 이에 대한 내용을 간략적으로 흉내내보자.

class A{
    [static] interface I{ // static이 있다면 A없이도 사용 가능
        void method();
    }
}


// 예시
// UI에서 버튼에 해당하는 클래스라고 가정
package sec01.exam01;

public class Button {	
	// 3. 인터페이스 타입의 필드 선언
	onClickListener listener;
	
	// 4. 외부로부터 구현 객체를 받도록
	void setListener(onClickListener listener) {
		this.listener = listener;
	}
	
	// 1. 버튼은 누르면 작동할 것
	// 버튼내용을 바로 적는게 아니라 개발자마다 사용용도가 다름
	void click() {
		listener.onClick();
	}
	
	// 2.
	// 버튼을 클릭했을 때 버튼의 실행 내용을 가지고 있는
	// 구현 객체를 얻기 위해서 인터페이스 선언
	static interface onClickListener{
		void onClick();
	}
}


// 인터페이스 구현 클래스
package sec01.exam01;

public class CallListener implements Button.onClickListener{

	@Override
	public void onClick() {
		System.out.println("전화를 겁니다");		
	}	
}

// 실행 메인 클래스
package sec01.exam01;

public class ButtonExam {
	public static void main(String args[]) {
		Button btn = new Button();
		
		// setListener의 인자로 주기 위해서는
		// onClickListener의 구현 객체가 필요함
		Button.onClickListener listener = new CallListener();
		btn.setListener(listener); // 여기에 new CallListener로 바로 넣어줘도 돼
		btn.click();
	}
}


5. 정리하기


  • 중첩 클래스 : 클래스 내부에 선언한 클래스
  • 멤버 클래스 : 클래스의 멤버로서 선언되는 중첩 클래스, 바깥 객체의 필요 여부에 따라 인스턴스 멤버 클래스, 정적 멤버 클래스로 구분
  • 로컬 클래스 : 생성자 또는 메소드 블록 내부에 선언된 중첩 클래스
  • 중첩 인터페이스 : 클래스의 멤버로 선언된 인터페이스, 주로 UI 프로그래밍에서 이벤트 처리 목적으로 활용

본 포스팅은 ‘혼자 공부하는 자바’를 읽고 공부한 내용을 바탕으로 작성하였습니다.


© 2021. By Backtony