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

DANI·2023년 10월 4일
0

JAVA를 공부해보자

목록 보기
7/29
post-thumbnail

📕 중첩클래스(nested class), 중첩인터페이스(nested interface)란?

중첩 클래스는 클래스 내부에 선언한 클래스입니다.

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

인터페이스도 클래스 내부에 선언할 수 있는데 이를 중첩 인터페이스라 합니다.

인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 인터페이스를 만들기 위해서입니다.

💻 중첩클래스 예시

class Outer{
	class Inner{ // 중첩 클래스
    }
}

💻 중첩인터페이스 예시

class Outer{
	interface Inner{ // 중첩 인터페이스
    }
}


📖 1. 중첩클래스의 분류

✅ 인스턴스 멤버 클래스

class Outer{
	class Inner{ ... }
}

Outer 객체를 생성해야만 사용할 수 있는 Inner 클래스

✅ 정적 멤버 클래스

class Outer{
	static class Inner{ ... }
}

Outer 클래스로 바로 접근할 수 있는 Inner 클래스

✅ 로컬 클래스

class Outer{
	void method(){
		static class Inner{ ... }
    }
}

method()가 실행할 때만 사용할 수 있는 Inner 클래스

멤버 클래스와 로컬 클래스도 하나의 클래스 이기 때문에 컴파일하면 바이트 코드 파일(.class)이 별도로 생성된다.

  • 멤버 클래스 : Outer $ Inner.class
  • 로컬 클래스 : Outer $1 Inner.class


🔍 1-1. 인스턴스 멤버 클래스


Class Outer{
	Class Inner{
    	Inner(){ } // 생성자
        int field; // 인스턴스 필드
        void method(){ } // 인스턴스 메소드
        // static int field2; 정적 필드(X)
        // static void method2() { } 정적 메소드(X)
    }
}

📥 Outer 클래스 외부

Outer outer = new Outer();
Outer.Inner inner = Outer.new Inner();
inner.field = 3;
inner.method();

📥 Outer 클래스 내부

Class Outer{
	Class Inner{
    	Inner(){ }
        int field;
        void method(){ }
    }
    
    void methodOuter(){
    Inner inner = new Inner();
    inner.field = 3;
    inner.method();
    }
}

일반적으로 Outer클래스 외부에서 Inner 객체를 생성하는 일은 거의 없다.



🔍 1-2. 정적 멤버 클래스


Class Outer{
	static Class Inner{
    	Inner(){ } // 생성자
        int field; // 인스턴스 필드
        void method(){ } // 인스턴스 메소드
        static int field2; // 정적 필드
        static void method2() { } // 정적 메소드
    }
}

📥 Outer 클래스 외부

Outer.Inner inner = new Outer.Inner();
inner.field = 3; // 인스턴스 필드 사용
inner.method(); // 인스턴스 메소드 호출
Outer.Inner.field = 2; // 정적필드 호출
Outer.Inner.method2(); // 정적메소드 호출

정적 멤버 클래스는 Outer객체를 생성할 필요 없이 Inner 객체를 생성할 수 있다.



🔍 1-3. 로컬 클래스


Class Outer{
	void method(){
    	
        class Inner {
        	Inner() { }
            int field1;
            void method1() { }
            // static int field2; 정적 필드 사용 불가
            // static void method2() { } 정적 메소드 사용 불가
		}		
	    
        Inner inner = new Inner();
        inner.field1 = 3;
        inner.method1();
}

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


📥 로컬 클래스 실행

void method() {
	class Inner extends Thread { ... }
    Inner thread = new Inner();
    thread.start();

로컬 클래스는 메소드가 실행될 때 메소드 내에서 객체를 생성하고 사용해야 한다. 주로, 비동기 처리를 위해 스레드 객체를 만들 때 사용



📖 2. 중첩클래스의 접근 제한


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

메서드의 매개 변수나 로컬 변수를 로컬 클래스에서 사용할 때 제한이 있다. 로컬 클래스의 객체는 메소드 실행이 종료되면 없어지는 것이 일반적이지만, 메소드가 종료되어도 계속 실행 상태로 존재할 수 있다. 예를 들어 로컬 스레드 객체를 사용할 때 이다. 이 문제를 해결하기 위해 컴파일 시 로컬 클래스에서 사용하는 매개 변수가 로컬 변수의 값을 로컬 클래스 내부에 복사해두고 사용한다. 그리고 이러한 매개 변수나 로컬 변수의 값이 수정되는 것을 방지하기 위해 final로 선언할 것을 요구한다.


💻 멤버 클래스에서 사용 제한

public class Outer {
	
	int field1;
	void method1() { }
	
	static int field2;
	static void method2() { }
	
	
	// 인스턴스 멤버 클래스
	class Inner{
		void method() {
			field1 = 10; // 인스턴스 필드 사용 가능
			method1(); // 인스턴스 메소드 호출 가능
			
			field2 = 10; // 정적 필드 사용 가능
			method2(); // 정적 메소드 호출 가능
		}
	}
	
	// 정적 멤버 클래스
	static class Inner2{
		void method() {
		//	field1 = 10; 인스턴스 필드 사용 불가능
		//	method1(); 인스턴스 메소드 호출 불가능
			
			field2 = 2;
			method2();
		}
	}
}

💻 로컬 클래스에서 사용 제한

public class Outter {
	
	public void method2(int arg) {
		
		// 로컬 변수
		int localVariable = 10; // final로 취급됨
		
		// arg = 100; final로 취급되기 때문에 수정X
		// localVariable = 100; final로 취급되기 때문에 수정X
		
		// 로컬 클래스
		class Inner {
			public void method() {
			// 로컬변수와 매개변수에 컴파일할 때 자동으로 final이 붙음
			int result = arg + localVariable;
			}
		}
	}
}

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


  • 바깥클래스.this.필드
  • 바깥클래스.this.메소드();
    public class Outter {
    	// 바깥클래스 필드
    	String field = "Outter-field";
    	// 바깥클래스 메소드
    	void method() {
    		System.out.println("Outter-method");
    	}	
    	// 중첩(내부)클래스
    	class Nested{
    		// 중첩 클래스 필드
    		String field = "중첨클래스-필드";
    		// 중첩 클래스 메소드
    		void method() {
    			System.out.println("중첩클래스-메소드");
    		}		
    		// 내부클래스와 외부클래스 멤버 출력
    		void print() {
    			// 중첩클래스 참조
    			System.out.println(this.field);
    			this.method();
    						// 바깥(외부) 클래스 멤버 참조
    			System.out.println(Outter.this.field);
    			Outter.this.method();
    		}
    	}
    }

📖 3. 중첩 인터페이스

중첩 인터페이스는 인스턴스 멤버 인터페이스와 정적 멤버 인터페이스 모두 가능하다.

Class Outer{
	[static] interface Inner {
    	void method();
    }
}

인스턴스 멤버 인터페이스는 바깥 클래스의 객체가 있어야 사용 가능하고, 정적 멤버 인터페이스는 바깥 클래스만으로 바로 접근할 수 있다. 주로 정적 멤버 인터페이스를 많이 사용하며 UI프로그래밍에서 이벤트를 처리할 목적으로 많이 활용된다.

💻 터치(touch())를 하면 이벤트가 발생하는 Button 클래스 구현

🔴 Button 클래스

public class Button {
	// 자바의 다중상속을 정의하는 메소드 선언
	// 버튼 클래스와 이벤트가 발생할 때 자동호출(콜백)
	// 되는 이벤트 핸들러의 연결코드
	
	OnClickListener listener; // 인터페이스 타입 필드
	
	// 인터페이스를 등록
	// 매개변수를 이용한 다형성
	void setOnClickListener(OnClickListener listener) {
		this.listener = listener;
	}
    
	// 실제 하드웨어로 구현하나
	// 지금은 시뮬레이션 화면이 있다고 보고 화면을 누르면
	void touch() {
		listener.onClick();	// 실제 위 인터페이스를 구현할 객체의 onClick() 메소드 호출
	}
	
    // 중첩 인터페이스
	static interface OnClickListener{
		void onClick();
	}
}

🔴 CallListener 클래스

// 터치를 이용하여 전화를 겁니다.
public class CallListener implements Button.OnClickListener {

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

🔴 MessageListener 클래스

// 터치를 이용하여 메세지를 보낸다.
public class MessageListener implements Button.OnClickListener{

	@Override
	public void onClick() {
		System.out.println("메세지를 보냅니다.");
	}
}

🔴 Main 클래스

public class ButtonExample {
	public static void main(String[] args) {
		Button button = new Button();
		
		// OnClickListener listener = new CallListener()
		button.setOnClickListener(new CallListener()); 
		button.touch();
		
		// OnClickListener listener = new MessageListener()
		button.setOnClickListener(new MessageListener());
		button.touch();
	}
}

🔵 실행 결과

전화를 겁니다.
메세지를 보냅니다.

💡 이모저모


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

0개의 댓글