Notice
Recent Posts
Recent Comments
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Archives
Today
Total
관리 메뉴

SYDev

[이것이 자바다] Chapter 9. 중첩 선언과 익명 객체 본문

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에서 중첩 인터페이스 예제 또한, 명시적인 구현 클래스 생성 없이 코드 중복도를 없앨 수 있음


참고자료