Programming Lang/Java
                
              [이것이 자바다] Chapter 9. 중첩 선언과 익명 객체
                시데브
                 2025. 1. 30. 19:31
              
              
            
            1. 중첩 클래스
- 중첩 클래스(Nested Class): 클래스 내부에 선언한 클래스
 - 클래스 멤버를 쉽게 사용할 수 있고, 외부에는 중첩 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있음
 
| 선언 위치에 따른 분류 | 선언 위치 | 객체 생성 조건 | |
| 멤버 클래스 | 인스턴스 멤버 클래스 | class A { class B { ... } }  | 
A 객체를 생성해야만  B 객체를 생성할 수 있음  | 
| 정적 멤버 클래스 | class A { static class B { ... } }  | 
A 객체를 생성하지 않아도 B 객체를 생성할 수 있음  | 
|
| 로컬 클래스 | class A { void method() { class B { ... } } }  | 
method가 실행할 때만  B 객체를 생성할 수 있음  | 
|
- 중첩 클래스도 하나의 클래스이기 때문에, 컴파일하면 바이트코드 파일(.class)이 별도로 생성됨
 - 멤버 클래스일 경우 바이트코드 파일의 이름은 다음과 같이 결정됨
 

- 로컬 클래스일 경우 바이트코드 파일의 이름은 다음과 같이 결정됨
 

2. 인스턴스 멤버 클래스
- 접근 제한자
- public class B {}: 다른 패키지에서 B 클래스 사용 가능
 - class B {}: 같은 패키지에서만 B 클래스 사용 가능
 - private class B {}: A 클래스 내부에서만 B 클래스 사용 가능
 
 - 주로 private 접근 제한을 갖는 것이 일반적
 
[public] class A {
    [public | private] class B {
    }
}
- 외부에서 생성하려면, A 객체 생성 이후, B 객체를 생성해야 함
 
A a = new A();
A.B b = a.new B();
- 예제
package ch9.sec2;
public class A {
	class B {
		int field1 = 1;
		
		// static field(Java 17부터 허용
		static int field2 = 2;
		
		B () {
			System.out.println("B-생성자 실행");
		}
		
		void method1() {
			System.out.println("B-method1 실행");
		}
		
		// static method(Java 17부터 허용
		static void method2() {
			System.out.println("B-method2 실행");
		}
	}
	
	// instance method
	void useB() {
		// B 객체 생성 및 인스턴스 필드 및 메소드 사용
		B b = new B();
		System.out.println(b.field1);
		b.method1();
		
		// B 클래스의 정적 필드 및 메소드 사용
		System.out.println(B.field2);
		B.method2();
	}
}
package ch9.sec2;
public class AExample {
	public static void main(String[] args) {
		// A 객체 생성
		A a = new A();
		
		A.B b = a.new B();
		
		// A 인스턴스 메소드 호출
		a.useB();
		
		b.method1();
		A.B.method2();
	}
}
3. 정적 멤버 클래스
- 접근 제한자
- public static class B {}: 다른 패키지에서 B 클래스 사용 가능
 - class B {}: 같은 패키지에서만 B 클래스 사용 가능
 - private static class B {}: A 클래스 내부에서만 B 클래스 사용 가능
 
 - 주로 default 또는 public 접근 제한을 가짐
 
[public] class A {
    [public | private] static class B {
    }
}
- A 클래스 외부에서 B 객체를 생셩하려면, A 객체 생성 없이 A 클래스로 접근해서 B 객체 생성 가능
 
A.B b = new A.B();
4. 로컬 클래스
- 로컬 클래스는 생성자와 메소드가 실행될 동안에만 객체를 생성 가능
 - 로컬 변수(생성자 또는 메소드의 매개변수 또는 내부에서 선언된 변수)를 로컬 클래스에서 사용할 경우 로컬 변수는 final 특성을 갖게되므로, 값을 읽을 수만 있고 수정할 수 없음
 
[public] class A {
    // 생성자
    public A() {
        class B { }
    }
    
    // 메소드
    public void method() {
        class B { }
    }
}
- 예제
package ch9.sec4;
public class A {
	// method
	void useB() {
		// local class
		class B {
			// instance field 
			int field1 = 1;
			
			// static field
			static int field2 = 2;
			
			// constructor
			B() {
				System.out.println("B-생성자 실행");
			}
			
			// instance method
			void method1() {
				System.out.println("B-method1 실행");
			}
			
			// static method
			static void method2() {
				System.out.println("B-method2 실행");
			}
		}
		
		// local object 생성
		B b = new B();
		
		// local object의 instance field와 method 사용
		System.out.println(b.field1);
		b.method1();
		
		// local class의 static field와 method 사용
		System.out.println(B.field2);
		B.method2();
	}
}
package ch9.sec4;
public class AExample {
	public static void main(String[] args) {
		A a = new A();
		
		a.useB();
	}
}
5. 바깥 멤버 접근
- nested class는 바깥 클래스와 긴밀한 관계를 맺으며, 바깥 클래스의 멤버(field, method)에 접근이 가능
 
5.1. 바깥 클래스의 멤버 접근 제한
| 구분 | 바깥 클래스의 사용 가능한 멤버 | 
| 인스턴스 멤버 클래스 | 바깥 클래스의 모든 필드와 메소드 | 
| 정적 멤버 클래스 | 바깥 클래스의 정적 필드와 정적 메소드 | 
5.2. 바깥 클래스의 객체 접근
바깥클래스이름.this // 바깥객체
6. 중첩 인터페이스
- 중첩 인터페이스: 클래스의 멤버로 선언된 인터페이스
 - 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해 사용
 
class A {
    [public | private] [static] interface B {
        // 상수 필드
        // 추상 메소드
        // 디폴트 메소드
        // 정적 메소드
    }
}
- 예제
- 안드로이드와 같은 UI 프로그램에서 이벤트를 처리할 목적으로 많이 활용
- ex) 버튼을 클릭했을 때, 이벤트를 처리할 객체는 중첩 인터페이스를 구현해서 생성
 
 
package ch9.sec6;
public class Button {
	// static nested interface
	public static interface ClickListener {
		// abstract method
		void onClick();
	}
	
	// field
	private ClickListener clickListener;
	
	// method
	public void setClickListener(ClickListener clickListener) {
		this.clickListener = clickListener;
	}
	
	public void click() {
		this.clickListener.onClick();
	}
}
package ch9.sec6;
public class ButtonExample {
	public static void main(String[] args) {
		// Ok 버튼 객체 생성
		Button btnOk = new Button();
		
		// Ok 버튼 클릭 이벤트를 처리할 ClickListener 구현 클래스(로컬 클래스)
		class OkListener implements Button.ClickListener {
			@Override
			public void onClick() {
				System.out.println("Ok 버튼을 클릭했습니다.");
			}
		}
		
		// Ok 버튼 객체에 ClickListener 구현 객체 주입
		btnOk.setClickListener(new OkListener());
		
		// Ok 버튼 클릭하기
		btnOk.click();
		
		//----------------------------------------------------------
		
		// Cancel 버튼 객체 생성
		Button btnCancel = new Button();
		
		// Cancel 버튼 클릭 이벤트를 처리할 ClickListener 구현클래스(로컬 클래스)
		class CancelListener implements Button.ClickListener {
			@Override
			public void onClick() {
				System.out.println("Cancel 버튼을 클릭했습니다.");
			}
		}
		
		//Cancel 버튼 객체에 ClickListener 구현 객체 주입
		btnCancel.setClickListener(new CancelListener());
		
		// Cancel 버튼 클릭하기
		btnCancel.click();
	}
}
7. 익명 객체
- 익명 객체(anonymous object): 이름이 없는 객체를 의미
- 명시적으로 클래스 선언 X -> 쉽게 객체 생성 가능
 - 필드값, 로컬 변수값, 매개변수값으로 주로 사용
 
 - 클래스를 상속하거나, 인터페이스를 구현해야만 생성 가능
- 익명 자식 객체: 클래스를 상속하는 경우
 - 익명 구현 객체: 인터페이스를 구현해서 만드는 경우
 
 
7.1. 익명 자식 객체
- 부모 타입의 필드, 로컬 변수, 매개변수의 값으로 대입될 수 있음
 
new 부모생성자( 매개값, ... ) {
    // 필드
    // 메소드
}
- 예제
package ch9.sec7;
public class Tire {
	public void roll() {
		System.out.println("일반 타이어가 굴러갑니다.");
	}
}
package ch9.sec7;
public class Car {
	// 필드에 Tire 객체 대입
	private Tire tire1 = new Tire();
	
	// 필드에 익명 자식 객체 대입
	private Tire tire2 = new Tire() {
		@Override
		public void roll() {
			System.out.println("익명 자식 Tire 객체 1이 굴러갑니다.");
		}
	};
	
	// method(field 이용)
	public void run1() {
		tire1.roll();
		tire2.roll();
	}
	
	// method(로컬 변수 이용)
	public void run2() {
		// 로컬 변수에 익명 자식 객체 대입
		Tire tire = new Tire() {
			@Override 
			public void roll() {
				System.out.println("익명 자식 Tire 객체 2가 굴러갑니다.");
			}
		};
		tire.roll();
	}
	
	// method(parameter 이용)
	public void run3(Tire tire) {
		tire.roll();
	}
}
package ch9.sec7;
public class CarExample {
	public static void main(String[] args) {
		// Car 객체 생성
		Car car = new Car();
		
		// 익명 자식 객체가 대입된 필드 사용
		car.run1();
		
		// 익명 자식 객체가 대입된 로컬변수 사용
		car.run2();
		
		// 익명 자식 객체가 대입된 매개변수 사용
		car.run3(new Tire() {
			@Override
			public void roll() {
				System.out.println("익명 자식 Tire 객체 3이 굴러갑니다.");
			}
		});		
	}
}
7.2. 익명 구현 객체
- 인터페이스 타입의 필드, 로컬변수, 매개변수의 값으로 대입될 수 있음
 
new 인터페이스() {
    // 필드
    // 메소드
}
- 안드로이드와 같은 UI 프로그램에서 이벤트를 처리하는 객체로 많이 사용
 
- 예제
package ch9.sec7.exam02;
public interface RemoteControl {
	// abstract method
	void turnOn();
	void turnOff();
}
package ch9.sec7.exam02;
public class Home {
	// 필드에 익명 구현 객체 대입
	private RemoteControl rc = new RemoteControl() {
		@Override
		public void turnOn() {
			System.out.println("TV를 켭니다.");
		}
		
		@Override
		public void turnOff() {
			System.out.println("TV를 끕니다.");
		}
	};
	
	// method(field 이용)
	public void use1() {
		rc.turnOn();
		rc.turnOff();
	}
	
	// method(로컬 변수 이용)
	public void use2() {
		// 로컬 변수에 익명 구현 객체 대입
		RemoteControl rc = new RemoteControl() {
			@Override 
			public void turnOn() {
				System.out.println("에어컨을 켭니다.");
			}
			
			@Override
			public void turnOff() {
				System.out.println("에어컨을 끕니다.");
			}
		};
		rc.turnOn();
		rc.turnOff();
	}
	
	// method(parameter 이용)
	public void use3(RemoteControl rc) {
		rc.turnOn();
		rc.turnOff();
	}
}
package ch9.sec7.exam02;
public class HomeExample {
	public static void main(String[] args) {
		// Home 객체 생성
		Home home = new Home();
		
		// 익명 구현 객체가 대입된 필드 사용
		home.use1();
		
		// 익명 구현 객체가 대입된 로컬 변수 사용
		home.use2();
		
		// 익명 구현 객체가 대입된 매개변수 사용
		home.use3(new RemoteControl() {
			@Override
			public void turnOn() {
				System.out.println("난방을 켭니다.");
			}
			
			@Override 
			public void turnOff() {
				System.out.println("난방을 끕니다.");
			}
		});
	}
}
-> 9.6에서 중첩 인터페이스 예제 또한, 명시적인 구현 클래스 생성 없이 코드 중복도를 없앨 수 있음
참고자료
728x90
    
    
  반응형