중첩 선언

이동주·2025년 3월 10일

JAVA

목록 보기
19/30

(3월 10일)

중첩 클래스

  • 클래스 내부에 클래스를 선언하는 것 : 일종의 멤버가 됨
  • 외부에서 중첩 관계 클래스를 감춤으로써 코드의 복잡성 최소화
  • 일종의 변수의 개념으로 볼 수도 있음
  • 멤버 클래스 : 클래스의 멤버로서 선언되는 중첩 클래스
  • 로컬 클래스 : 메소드의 내부에서 선언되는 중첩 클래스
    -> 해당 메소드 안에서만 사용 가능!
  • bin 파일 명에 중첩 클래스는 A$B.class 와 같은 형식으로 저장됨
  • 로컬 클래스는 메소드의 순서에 따라 A$1B, A$2B와 같이 이름을 정함

인스턴스 멤버 클래스

  • A 클래스의 멤버로 선언된 B 클래스
public class A {
	class B {
    	// 인스턴스 멤버 클래스
        // 접근 방식에 따라 외부에서 사용할 수 있는 범위가 다름
        // public : 다른 패키지에서도 사용 가능
        // (default) : 
    }
}
  • 인스턴스 멤버 클래스 B는 주로 클래스 A 내부에서 사용되므로 private 접근 제한을 가짐

A클래스의 인스턴스 멤버 클래스 B

package ch09.sec02.exam01;

public class A {
	class B {
		// 인스턴스 멤버 클래스 : 클래스 A 내부에 정의된 일반적인 내부 클래스
		// 주로 private를 사용하여 외부에서 직접 접근을 막음
		// A클래스 내부에 있는 B클래스의 객체는 인스턴스 멤버 클래스 이므로
		// 인스턴스 외부 클래스의 멤버변수나, 생성자, 메소드에서만 생성 가능
	}
	
	B field = new B();
	// A클래스의 인스턴스 필드에 B 객체 생성 가능
	// A의 인스턴스가 존재해야 field가 생성 가능
	
	A(){
		B b = new B();
	}
	// 기본생성자 A를 통해 B객체를 생성하여 호출할 수 있음
	
	void method() {
		B b = new B();
	}
	// 클래스 내부의 메소드에서도 B 객체 생성 가능
}

실행문

package ch09.sec02.exam01;

public class Aexam {
	public static void main(String[] args) {
		A a = new A();
		// B클래스는 A의 인스턴스 멤버 클래스이다.
		// 정적(static)인 객체가 아니기 때문에
		// 외부클래스 A의 객체가 존재해야 생성이 가능하다
		
		A.B b = a.new B();
		// 외부클래스.내부클래스 참조변수(내부) = 참조변수(외부).new 내부클래스명();
		// 인스턴스 a를 통해 B객체를 생성하는 과정
	}
}

인스턴스 멤버 클래스 안에 정적 필드 및 메소드 선언

A의 인스턴스 멤버 클래스 B

package ch09.sec02.exam02;

public class A {
	
	// class B : A의 인스턴스 멤버 클래스
	class B {
		int field1 = 1;
		// 인스턴스 필드 선언
		
		static int field2 = 2;
		// 정적 필드 선언 (Java 17 부터 가능)
		
		B(){
			System.out.println("B-생성자 실행");
		}
		// B() 생성자
		
		void method1() {
			System.out.println("B-method1 실행");
		}
		// 인스턴스 메소드
		
		static void method2() {
			System.out.println("B-method2 실행");
		}
		// 정적 메소드 (Java 17 부터 가능)
	}
	
	// A 클래스의 인스턴스 메소드
	void useB() {
		// B 객체 생성
		B b = new B();
		// 인스턴스 필드 
		System.out.println(b.field1);
		// 메소드 사용
		b.method1();
		
		// 정적 필드 선언 : static을 사용했기 때문에 해당 클래스명을 그대로 명시해주기
		System.out.println(B.field2);
		// 정적 메소드 선언 : static을 사용했기 때문에 해당 클래스명을 그대로 명시해주기
		B.method2();
	}
}

클래스 실행

package ch09.sec02.exam02;

public class AExample {
	public static void main(String[] args) {
		A a = new A();
		// A 객체 생성함으로서 A 인스턴스 사용 가능
		
		a.useB();
		// A의 인스턴스 메소드 useB 호출함으로서
		// useB 내부에 생성된 B객체를 사용할 수 있음
	}
}

정적 멤버 클래스

  • static 키워드와 함께 A 클래스의 멤버로 선언된 B 클래스
  • 정적 멤버 클래스는 주로 default 또는 public 접근 제한을 가짐
public class A {
	static class B {
    	// 정적 클래스이기 때문에 A 객체가 존재하지 않아도 됨
    }
}

A 클래스 내부의 정적 멤버 클래스 B

package ch09.sec03.exam01;

public class A {
	static class B{
		// 정적 멤버 클래스 선언
		// 정적 클래스이기 때문에 A객체를 생성하지 않아도 사용가능
		// 정적 멤버 클래스는 외부 클래스 어디에서든 생성 가능
	}
	
	B field1 = new B();
	// B 객체를 인스턴스 필드에 생성
	
	static B field2 = new B();
	// 정적 필드 값으로 B 객체를 대입함
	// B가 정적 클래스이기 때문에 정적 필드에 객체 생성 가능
	
	A(){
		B b = new B();
	}
	// 생성자 A에 B 객체 생성
	// A 생성자를 통해 B 객체를 호출할 수 있음
	
	void method1() {
		B b = new B();
	}
	// 인스턴스 메소드에 B 객체 생성
	
	static void method2() {
		B b = new B();
	}
	// 정적 메소드에 B 객체 생성
	// B가 정적 클래스이기 때문에 정적 메소드에 객체 생성 가능
}

B 객체 생성

package ch09.sec03.exam01;

public class AExample {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		A.B b = new A.B();
		// A클래스 내부에 있는 B 객체 생성
		// B클래스는 정적 멤버 따로 외부클래스(A)를 생성할 필요가 없고
		// 독립적으로 생성이 가능함
	}

}

내부 클래스의 정적 메소드와 정적 필드 호출하기

정적 멤버 클래스에 인스턴스, 정적 필드, 메소드 등 생성

package ch09.sec03.exam02;

public class A {
	// 정적 멤버 클랫
	static class B {
		int field1 = 1;
		// 인스턴스 필드 생성
		
		static int field2 = 2;
		// 정적 필드 생성 (Java 17부터 가능)
		
		B(){
			System.out.println("B-생성자");
		}
		// B 생성자 선언
		
		void method1() {
			System.out.println("B-method1");
		}
		// 인스턴트 메소드 생성
		
		static void method2() {
			System.out.println("B-method2");
		}
		// 정적 메소드 생성 (Java 17부터 가능)
	}
}

실행 클래스

package ch09.sec03.exam02;

public class AExample {
	public static void main(String[] args) {
		A.B b = new A.B();
		// A클래스 내부에 있는 B 객체 생성
		// B가 정적 멤버 클래스이기 때문에 외부 클래스를 따로 생성할 필요 없이
		// 독립적으로 생성이 가능함
		
		System.out.println(b.field1);
		// B 객체에 저장된 field1 값 호출
		// 인스턴스 필드를 호출하는 것이기 때문에 참조변수를 통해 선언
		
		b.method1();
		// B 객체에 저장된 method1 호출
		// 인스턴스 메소드를 호출하는 것이기 때문에 참조변수를 통해 선언
		
		
		System.out.println(A.B.field2);
		// B 객체에 저장된 field2 값 호출
		// 정적 필드를 호출하는 것이기 때문에 클래스를 통해 선언
		
		A.B.method2();
		// B 객체에 저장된 method2 메소드 호출
		// 정적 메소드를 호출하는 것이기 때문에 클래스를 통해 선언
	}
}

로컬 클래스

  • 생성자 또는 메소드 내부에서 선언된 클래스
  • 생성자 메소드가 실행될 동안에만 객체를 생성할 수 있음
  • 지역 변수를 로컬 클래스에 접근할 때 컴파일러에 의해 자동적으로 final 이라는 키워드가 붙음
    -> 해당 변수의 값을 변경할 수 없음
public class A {	
	// 생성자
    public A() {
    	class B { }
    }
    
    // 메소드
    public void method() {
    	class B { }
    }
}

메소드 내부에 생성된 로컬 클래스

package ch09.sec04.exam02;

public class A {
	void useB() {
		// 로컬 클래스
		class B {
			int field1 = 1;
			static int field2 = 2; // java 17 부터 가능
			B(){
				System.out.println("B 생성자");
			}
			
			void method1() {
				System.out.println("B-method1");
			}
			
			static void method2() {
				System.out.println("B-method2");
			} // java 17 부터 가능
		}
		
		B b = new B();
		// 메소드 내부에서 로컬 클래스 생성 가능
		
		System.out.println(b.field1);
		b.method1();
		// 인스턴스 필드 및 메소드 선언
		
		System.out.println(B.field2);
		B.method2();
		// 정적 필드 및 메소드 선언
	}
}

실행 클래스

package ch09.sec04.exam02;

public class AExample {
	public static void main(String[] args) {
		A a = new A();
		// A 객체 생성
		
		a.useB();
		// A 클래스 내부에 생성된 useB 메소드 안에
		// 로컬 클래스 B가 생성되었기 때문에
		// useB 메소드를 선언함으로서 B 객체를 생성할 수 있음
	}
}

로컬 변수를 로컬 클래스에 접근할 때

package ch09.sec04.exam03;

public class A {
	public void method1(int arg) {
		int var = 1;
		
		// int arg, var : 지역 변수
		// Java에서 로컬 변수는 fianl로 취급함
		
		class B {
			void method2() {
				System.out.println("arg: " + arg);
				System.out.println("var: " + var);
				// 지역 변수 출력
				// 지역 변수를 로컬 클래스 내부에서 사용 가능
				
				// 하지만 지역 변수는 로컬 클래스에 접근할 때 final의 형태로 변환되므로
				// 값을 수정할 수 없음
//				arg = 2;
//				var = 2;
			}
		}
		
		B b = new B();
		b.method2();
		
		// 지역 변수는 final 취급이 되었으므로 값 변경 안됨
//		arg = 3;
//		var = 3;
	}
}

바깥 클래스

  • 정적 멤버 클래스 내부에서는 바깥 클래스의 필드와 메소드를 사용할 때 제한이 따름
  • 정적 멤버 클래스는 바깥 객체가 없어도 사용 가능해야 하므로 바깥 클래스의 인스턴스 필드와 인스턴스 메소드는 사용 못함

바깥 클래스의 사용 가능한 멤버

  • 인스턴스 멤버 클래스 : 바깥 클래스의 모든 필드와 메소드
  • 정적 멤버 클래스 : 바깥 클래스의 정적 필드와 정적 메소드
package ch09.sec05.exam01;

public class A {
	// A의 인스턴스 필드와 메소드
	int field1;
	void method1() { }
	
	// A의 정적 필드와 메소드
	static int field2;
	static void method2() { }
	
	// 인스턴스 멤버 클래스
	class B{
		void method() {
			// A의 인스턴스 필드와 메소드 사용
			field1 = 10;
			method1();
			
			// A의 정적 필드와 메소드 사용
			field2 = 10;
			method2();
		}
		// 인스턴스 멤버 클래스에는 바깥 클래스의 정적, 인스턴스 필드, 메소드 등 모두 선언 가능
	}
	
	static class C {
		void method() {
			// A의 인스턴스 필드와 메소드 사용
//			field1 = 10;
//			method1();
						
			// A의 정적 필드와 메소드 사용
			field2 = 10;
			method2();
		}
		// 정적 멤버 클래스에는 바깥 클래스의 정적 필드, 메소드 등만 선언 가능
	}
}

바깥 클래스의 객체 접근

  • 중첩 클래스 내부에서 바깥 클래스의 객체를 얻으려면 바깥 클래스 이름에 this를 붙임!
    -> 바깥클래스이름.this
package ch09.sec05.exam02;

public class A {

	// A의 인스턴스 필드
	String field = "A-field";
	
	// A의 인스턴스 메소드
	void method() {
		System.out.println("A-method");
	}
	
	class B{
		// B의 인스턴스 필드
		String field = "B-field";
		
		// B의 인스턴스 메소드
		void method() {
			System.out.println("B-method");
		}
		
		// B의 인스턴스 메소드
		void print() {
			// this문의 경우 현재 객체에 있는 필드를 선언하기 때문에
			// B 객체의 필드와 메소드를 사용함
			System.out.println(this.field);
			this.method();
			
			// A.this : A 클래스 객체에 있는 필드를 선언함
			// A 객체의 필드와 메소드를 사용
			System.out.println(A.this.field);
			A.this.method();
		}
	}
	
	// A의 인스턴스 메소드
	void useB() {
		B b = new B();
		b.print();
	}
}

실행 클래스

package ch09.sec05.exam02;

public class AExam {
	public static void main(String[] args) {
		A a = new A();
		a.useB();
		// useB 메소드 내부에서 B 객체가 생성됨
	}
}

(3월 11일)

중첩 인터페이스

  • 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해 클래스의 멤버로 선언된 인터페이스
  • 안드로이드와 같은 UI 프로그램에서 이벤트 목적으로 처리할 목적으로 많이 활용됨
  • 이벤트 핸들러라고도 함 : 이벤트(사건)가 발생했을 때 처리하는 개념 (Listener라고도 함)
package ch09.sec06;

public class Button {
	// 정적 멤버 인터페이스
	public static interface ClickListener {
		void onClick();
		// 추상 메소드
	}
	
	// 필드 생성
	// 인터페이스는 객체 생성이 불가하므로 참조변수 형태로 선언해야함
	private ClickListener clickListener;
	
	// 인터페이스 대입 메소드
	// 인터페이스의 필드를 매개변수로 사용
	public void setClickListener(ClickListener clickListener) {
		this.clickListener = clickListener;
		// 입력받은 매개변수를 인터페이스 필드에 대입
	}
	
	// 인터페이스 동작 메소드
	// 버튼이 눌렸을 때 동작함
	public void click() {
		this.clickListener.onClick();
	}
}
package ch09.sec06;

public class ButtonExam {
	public static void main(String[] args) {
		Button btnOk = new Button();
		// OK 버튼 참조변수에 Button 객체 생성 (OK 버튼 생성)
		
		// Button 클래스의 ClickListener 인터페이스 참조
		// 인터페이스를 객체로 만들 수 없기 때문에 인터페이스 자체를 사용할 객체 생성
		// OK버튼 실행 클래스
		class OkListener implements Button.ClickListener {
			// ClickListener 인터페이스의 onClick 메소드 재정의
			@Override
			public void onClick() {
				System.out.println("OK 버튼을 생성했습니다.");
			}
		}
		
		// OkListener 객체를 setClickListener 메소드의 매개변수로 사용
		btnOk.setClickListener(new OkListener());
		
		// click 메소드 실행 및 ok 버튼 클릭
		btnOk.click();
		
		// Cancel 버튼 참조변수에 Button 객체 생성(Cancel 버튼 생성)
		Button btnCancel = new Button();
		
		// Button 클래스의 ClickListener 인터페이스 참조
		// Cancel버튼 실행 클래스
		class CancelListener implements Button.ClickListener {
			// ClickListener 인터페이스의 onClick 메소드 재정의
			@Override
			public void onClick() {
				System.out.println("Cancel 버튼을 클릭함");
			}
		}
		
		// CancelListener 객체를 setClickListener 메소드의 매개변수로 사용
		btnCancel.setClickListener(new CancelListener());
		
		// click 메소드 실행 및 cancel 버튼 클릭
		btnCancel.click();
	}
}

익명 객체

  • 이름이 없는 객체, 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체 생성 가능
  • 필드값, 로컬변수값, 매개변수값으로 주로 사용함
  • 주로 일회성으로 객체를 사용할 때 생성하며 외부에서 접근을 방지하기 위해서도 사용
new 부모생성자 {
	// 필드
    // 메소드
}

익명 자식 객체

  • 부모 클래스를 상속 받아 생성되는 객체
  • 부모 타입의 필드, 로컬 변수, 매개변수의 값으로 대입할 수 있음
package ch09.sec07.exam01;

public class Tire {
	// roll 메소드 선언
	public void roll() {
		System.out.println("일반 타이어가 굴러갑니다");
	}
}
package ch09.sec07.exam01;

public class Car {
	private Tire tire1 = new Tire();
	
	// 익명 객체 : 익명 클래스를 객체를 생성하여 참조변수에 바로 대입
	// 객체를 일회성으로 사용할 경우에 용이함
	private Tire tire2 = new Tire() {
		// 익명 객체에서 메소드 재정의함
		@Override
		public void roll() {
			System.out.println("익명 자식 Tire 객체 1이 굴러갑니다");
		}
	};
	// 사실상 값의 형태이기 때문에 세미콜론을 붙여줘야함
	
	public void run1() {
		tire1.roll();
		tire2.roll();
	}
	
	public void run2() {
		// 익명 객체 생성
		Tire tire = new Tire() {
			// Tire 클래스의 roll() 메소드를 재정의함
			@Override
			public void roll() {
				System.out.println("익명 자식 Tire 객체 2가 굴러갑니다");
			}
		};
		// 값의 형태이기 때문에 세미콜론을 붙여줘야함
		tire.roll();
	}
	
	public void run3(Tire tire) {
		tire.roll();
	}
}
package ch09.sec07.exam01;

public class CarExample {
	public static void main(String[] args) {
		Car car = new Car();
		car.run1();
		car.run2();
		
		// 메소드에도 익명 객체를 선언할 수 있음
		// 객체를 일회용으로 사용할 때 주로 사용
		car.run3(new Tire() {
			// Tire 객체의 roll 메소드를 재정의함
			@Override
			public void roll() {
				System.out.println("익명 자식 Tire 객체 3이 굴러갑니다.");
			}
		});
	}
}

익명 구현 객체

  • 인터페이스를 구현해서 생성되는 객체
  • 인터페이스 타입의 필드, 로컬변수, 매개변수의 값으로 대입할 수 있음
  • 안드로이드와 같은 UI 프로그램에서 이벤트를 처리하는 객체로 많이 사용함

형태

new 인터페이스(){
	//필드
    //메소드
}
package ch09.sec07.exam02;

public interface RemoteControl {
	// 인터페이스의 추상메소드 선언
	void turnOn();
	void turnOff();
}
package ch09.sec07.exam02;

public class Home {
	// 익명 구현 객체 : 익명으로 객체를 생성하여 그 안의 메소드를 직접 구현하는 것
	// 인터페이스는 원래 객체 생성이 불가능하나 익명 구현 객체는
	// 인터페이스를 구현한 이름 없는 클래스의 인스턴스이기 때문에 객체 생성이 가능
	// 인터페이스나 추상메소드를 구현할 때 주로 사용
	private RemoteControl rc = new RemoteControl() {
		public void turnOn() {
			System.out.println("TV를 켭니다");
		}
		
		public void turnOff() {
			System.out.println("TV를 끕니다");
		}
	};
	// 값의 형태이기 때문에 꼭 세미콜론 붙여주기
	
	public void use1() {
		rc.turnOn();
		rc.turnOff();
	}
	
	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();
	}
	
	public void use3(RemoteControl rc) {
		rc.turnOn();
		rc.turnOff();
	}
}
package ch09.sec07.exam02;

public class HomeExample {
	public static void main(String[] args) {
		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("난방을 끕니다");
			}
		});
	}
}
profile
끄작끄작

0개의 댓글