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

JUJU·2024년 1월 21일

✏️ 중첩 클래스

중첩 클래스란, 클래스 내부에 선언한 클래스이다.

  • 중첩 클래스를 사용하면 두 클래스의 멤버들을 쉽게 접근할 수 있다.
  • 여러 클래스와 관계를 맺는 경우는 독립적인 클래스를 선언하는 것이 좋지만, 특정 클래스와 관계를 맺는 경우 중첩 클래스가 좋다.
  • 중첩 클래스는 멤버 클래스와 로컬 클래스로 나뉜다.

1. 멤버 클래스

멤버 클래스는 클래스의 멤버로서 선언된 중첩 클래스이다.

멤버 클래스는 인스턴스 멤버 클래스와 정적 멤버 클래스로 나뉜다.

인스턴스 멤버 클래스

  • static 키워드 없이 선언된 중첩 클래스
  • 인스턴스 필드, 메소드만 선언 가능
  • Why? 정적 필드와 메소드는 객체 생성 없이도 실행 가능해야하기 때문
Class A{
	Class B{
    	B(){} // 생성자
        int fieldB; // 인스턴스 필드
        void methodB() { }; // 인스턴스 메소드
        
        /* 
        static int field;
        static void method() { }; 는 정적 필드와 메소드이므로 선언 불가능
        */
    }
}

A 클래스 내부에서 B 클래스를 사용하는 경우, 일반 클래스처럼 B 객체를 생성해서 사용한다.

Class A{
	Class B{
    	...
    }
    void methodA(){
    	B b = new B();
        b.fieldB = 3;
        b.methodB();
    }
}

A 클래스 외부에서 B 클래스를 사용하고 싶은 경우, A 객체 생성 후 B 객체를 생성해야 한다.
하지만, 일반적으로 A 클래스 외부에서 B 클래스를 사용하는 경우는 없다.

// A 클래스 외부
A a = new A();
A.B b = a.new B();
// 


정적 멤버 클래스

  • static 키워드로 선언된 클래스
  • 모든 종류의 필드와 메소드 선언 가능
Class A{
	Class C{
    	C(){} // 생성자
        int fieldC; // 인스턴스 필드
        void methodC; // 인스턴스 메소드
       
        static int field; // 정적 필드
        static void method() { }; // 정적 메소드
        
    }
}

A 클래스 외부에서 C 클래스를 사용하는 경우

A.C c = new A.C();
c.fieldC = 3;
c.method();

A.C.field = 3;
A.C.method() { }

2. 로컬 클래스

로컬 클래스는 생성자 또는 메소드 내부에서 선언된 중첩 클래스이다.

  • 로컬 클래스는 접근 제한자 및 static을 붙일 수 없다. --> 메소드 내부에서만 사용되기 때문
  • 정적 필드와 메소드는 선언할 수 없다.
void method(){
	// 생성자 또는 메소드 내부에서
    class D{
    	D(){ }
        int fieldD;
        void methodD() { }
        
        /*
        static int field
        static void method() { } 는 정적 필드와 메소드이므로 선언 불가
        */
    }
    
    D d = new D();
    d.fieldD = 3;
    d.mehtodD();
}


✏️ 중첩 클래스의 사용 제한

[바깥쪽 --> 안쪽]

1. Outter 클래스 --> 인스턴스 멤버 클래스

  • 인스턴스 멤버 객체를 정적 필드로 선언 불간능
  • 정적 메소드에서 인스턴스 멤버 객체를 사용 불가능
  • Outter 클래스의 인스턴스가 없으면 인스턴스 멤버 클래스의 인스턴스도 생성할 수 없기 때문
public class A{
	class B{ } // 인스턴스 멤버 클래스
    
    B field = new B();
    
    void methodA(){
    	B var = new B();
    }
    
    
    /*
    static B field = new B();
    static void methodSt(){
    	 B var = new B();
    }
    B의 객체를 정적 필드나 정적 메소드의 변수로는 사용 못함
    */
}

2. Outter 클래스 --> 정적 멤버 클래스

  • 인스턴스 멤버 객체를 정적 필드로 선언 가능
  • 정적 메소드에서 인스턴스 멤버 객체를 사용 가능
public class A{
	static class C{ } // 정적 멤버 클래스
    
    C field = new C();
    
    void methodA(){
    	C var = new C();
    }
    
    
    
    static C field2 = new C();
    static void methodSt(){
    	 C var2 = new C();
    }
}

3. Outter 클래스--> 로컬 클래스

  • 접근 불가능

[ 안쪽 --> 바깥쪽]

1. 인스턴스 멤버 클래스--> Outter 클래스

  • 제한 없음
class A{
	int fieldA;
    void methodA() {}
    
    static int fieldAS;
    static void methodAS() {}
    
    // Inner --> Outter 접근
    class B{
    	void methodB(){
        	fieldA = 3;
            methodA();
            
            fieldAS = 3;
            methodAS();
        }
    }
}

2. 정적 멤버 클래스--> Outter 클래스

  • Outter 클래스의 정적 멤버에만 접근 가능
class A{
	int fieldA;
    void methodA() {}
    
    static int fieldAS;
    static void methodAS() {}
    
    // Inner --> Outter 접근
    static class C{
    	void methodB(){
        	/*
        	fieldA = 3;
            methodA();
            인스턴스 필드와 메소드에는 접근 불가
            static class C는 A 객체 생성 없이도 사용 가능해야 하기 때문이다.
            */
            
            fieldAS = 3;
            methodAS();
        }
    }
}

3. 로컬 클래스--> Outter 클래스

  • 로컬 클래스는 메소드 실행이 종료되면 없어져야 함.
  • ⚠️ BUT!! 메소드가 종료되어도 존재하는 경우가 있음 - 로컬 스레드 객체
  • 위의 문제를 해결하기 위해, 컴파일 시 로컬 클래스의 변수를 로컬 클래스 내부에 복사해 둔다.
public class Outter{
	public void method(int arg){
    	int localVaribale = 1;
        
        class Inner{
        	public void methodI(){
            	int result = arg + localVariable;
                
                /* 
				로컬 클래스에서 사용하는 Outter 클래스의 변수 arg, localVariable
                두 변수의 값은 로컬 클래스 내부에 복사됨
                복사된 값과 Outter의 변수 값이 달라지면 안되므로, 변수의 값 수정 불가능
                */
            }
        }
    }
}

arg, localvariable 변수는 값이 달라져서는 안된다.
--> final로 선언해야 한다.

하지만, Java 8부터는 final 키워드를 붙이지 않아도 자동으로 final 특성이 부여된다.



✏️ 중첩 인터페이스

중첩 인터페이스란, 클래스의 멤버로 선언된 인터페이스이다.

  • 클래스 내부에 선언하는 이유는, 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서다.
  • 중첩 인터페이스의 종류로는 인스턴스 멤버 인터페이스와 정적 멤버 인터페이스가 있다.
  • 주로 정적 멤버 인터페이스를 사용하여 UI 프로그래밍에서 이벤트를 처리한다.
public class Button{
	// 중첩 인터페이스 OnClickListener
	static interface OnClickListener{
		void onClick(); // 구현 클래스는 반드시 onClick을 구현해야 한다.
	}
    
    OnClickListener listener;
    
    void setOnClickListener(OnClickListener listener){
    	this.listener = listener;
    }
    
    void touch(){
    	listener.onClick();
    }
}
// 구현 클래스
public class CallListener implements Button.OnClickListener{
	@Override
    public void onClick(){
    	System.out.println("calling");
    }
}
// 실행 클래스
public class Execute{
	public static void main(String[] args){
    	Button btn = new Button();
        
        btn.setOnClickListener(new CallListener());
        // 중첩 인터페이스 OnClickListener를 구현한 객체만 인수로 쓸 수 있음
        btn.touch();
    }
}


REFERENCE

혼자 공부하는 자바

profile
백엔드 개발자

0개의 댓글