중첩클래스와 중첩인터페이스

서현서현·2022년 3월 29일
0

JAVA

목록 보기
12/27
post-thumbnail

🥕 중첩클래스와 중첩인터페이스

(CF) 어플개발할때 이너클래스 자주 사용한다

보통은 하나의 자바파일을 정의할때 하나의 클래스만 선언하는 경우가 많음

이너클래스 말고 클래스 바깥 다른곳에도 클래스를 또 정의할 수 있는데 그럼 두 클래스는 그냥 동시에 존재하고 있는거다~ 두번째 클래스는 public 못붙이고 걍 class 클래스명임! (일반적 X)

혀튼 많이 쓰는건 아니고... 그냥 클래스는 다양한곳에 선언 할 수 있다는 개념을 알도록 하자

🥕 1. 소개

중첩클래스란? 클래스 내부에 선언한 클래스. 사용시 두 클래스 멤버들을 서로 쉽게 접근 할 수 있게 하고 외부에는 불필요한 관계클래스를 감춤으로서 코드의 복잡성을 줄일 수 있다.

인터페이스도 클래스 내부에 선언 할 수 있는데, 이를 중첩인터페이스 라고 한다. 사용하는 이유는 해당 클래스와 긴밀한 관계를 갖는 구현 클래스를 만들기 위해서이다.

🥕 중첩클래스

  • 클래스 내부에 선언되는 위치에 따라서 멤버클래스와 로컬클래스로 분류된다.
  • 클래스의 멤버로서 선언되는 중첩클래스가 멤버클래스이고
  • 생성자 또는 메소드 내부에서 선언되는 중첩클래스가 로컬클래스이다.
  • 멤버클래스는 클래스나 객체가 사용중이면 언제든지 재사용이 가능하나, 로컬클래스는 메소드를 실행할때만 사용되고 메소드가 종료되면 값이 없어진다.

  • 중첩클래스도 하나의 클래스이므로 컴파일하면 바이트코드파일이 별도로 생성된다
- 멤버클래스의 경우 바이트코드파일의 이름
A $ B .class
- 로컬클래스의 경우 바이트코드파일의 이름
A $1 B .class

인스턴스 멤버 클래스

  • 인스턴스멤버클래스는 static 없이 중첩 선언된 클래스이다.
  • 인스턴스 필드와 메소드만 선언이 가능하고 정적필드와 메소드는 선언 할 수 없다.
class A {
	class B{
		B(){...}
		int field1;
		//static int field2; << static 사용불가
		void method1() {}
		//static void method2() {} << static 사용불가
	}
}
A클래스 외부에서 B객체를 생성하려면 A객체부터 생성 하고 B객체를 생성해야한다. 
A클래스 내부의 생성자 및 인스턴스 메소드에서는 일반클래스처럼 B객체를 생성 할 수 있다.

[A클래스 외부]
A a = new A(); << A부터 생성
A.B b = a.new B();
b.field1 = 3;
b.method1();

[A클래스 내부]
class A {
	class B{
		void methodA() {}
		B b = new B();
		b.field1 = 3;
		b.method1();
	}
}

일반적으로 외부에서 생성하는 일은 거의 없고 A클래스 내부에서 B객체 생성하는게 대부분

정적 멤버 클래스

  • static 키워드로 선언된 클래스를 말한다.
  • 모든종류의 필드와 메소드를 선언 할 수 있다
class A {
	static class C {
		C() { } << 생성자
		int field1; << 인스턴스필드
		static int field2; << 정적필드
		void method1() {} << 인스턴스메소드
		static void method2 {} << 정적메소드
	}
}
A클래스 외부에서 정적멤버클래스 C의 객체를 생성하기 위해서는 A객체를 생성 할 필요가 없고 C를 생성

A.C c = new A.C();
c.field1 = 3;
c.method();
A.C.field2=3;
A.C.method2 ();

로컬클래스

  • 메소드내에서 선언된 중첩클래스
  • public, private, static을 붙일 수 없다. (메소드 내부에서만 사용되므로 접근제한할 필요 X)
  • 로컬클래스 내부에는 인스턴스필드와 메소드만 선언 할 수 있고 정적필드와 메소드는 선언 할 수 없다.
void method() {
	class D{
		D() {}
		int field1;
		//static int field2;
		void method1() {}
		//static void method2() {}
	}
	D d = new D();
	d.field1 = 3;
	d.method1();
}
주로 다음과같이 미동기 처리를 위한 스레드 객체를 만들 때 사용한다.
void method() {
	class DownloadThread extends Thread {...}
	DownloadThread thread = new DownloadThread();
	thread.start();
}
// 바깥클래스
class A {	
	A() {System.out.println("A객체가 생성됨");}

//인스턴스멤버클래스
class B {
	B() {System.out.println("B객체가 생성됨");}
	int field1;
	// static int field2;
	void method1() {}
	// void method2() {}
}

//정적멤버클래스
static class C {
	C() {System.out.println("C 객체가 생성됨");}
	int field1;
	static int field2;
	void method1() {}
	static void method2() {}
}

void method() {
//로컬클래스
	class D {
		D(){System.out.println("D 객체가 생성됨");}
		int field1;
		// static int field2;
		void method1() {}
		//static void method2() {}
	}
	D d = new D();
	d.field1 = 3;
	d.method1();
}
}

public class Main {
	public static void main(String[] args) {
		A a = new A();
		
		// 인스턴스멤버클래스 객체생성
		A.B b = a.new B();
		b.field1 = 3;
		b.method1();
		
		//정적 멤버 클래스 객체 생성
		A.C c = new A.C();
		c.field1 = 3;
		c.method1();
		A.C.field2 = 3;
		A.C.method2();
		
		//로컬클래스 객체 생성을 위한 메소드 호출
		a.method();
	}

}

🥕 중첩클래스의 접근제한

  • 멤버클래스의 내부에서 바깥 클래스의 필드와 메소드에 접근시 제한이 있다.
  • 메소드의 매개변수나 로컬변수를 로컬클래스에서 사용할때도 제한이 있다.

바깥필드와 메소드에서 사용제한

  • 바깥클래스에서 인스턴스멤버 클래스를 사용 할 때 제한이 있다.
인스턴스멤버 클래스(B)는 바깥클래스의 인스턴스(field1)의 초기값이나 
인스턴스메소드(method1())에서 객체를 생성 할 수 있으나
정적필드(field3)의 초기값이나 정적메소드(method2())에서는 객체를 생성 할 수 없다
반면, 정적멤버클래스(C)는 모ㅗ든필드의 초기값이나 모든 메소드에서 객체 생성 가능
public class A {
	// 인스턴스 필드
	 B field1 = new B();
	 C field2 = new C();
	// 인스턴스 메소드
	void method1(){
		B var1 = new B();
		C var2 = new C();
	}
	// 정적 필드 초기화
	//static B field3 = new B();
	static C field4 = new C();

	// 정적메소드
	static void method2() {
		//B car1 = new B();
		C var2 = new C();
	}
	//인스턴스멤버클래스
	class B():

	//정적멤버클래스
	static class C();

멤버클래스에서 사용제한

  • 멤버클래스가 인스턴스 또는 정적으로 선언됨에 따라 멤버클래스 내부에서 바깥클래스의 필드와 메소드에 접근할때도 제한이 있다. 인스턴스멤버 클래스(B) 안에서는 바깥클래스의 모든 필드와 메소드에 접근 할 수 있지만, 정적멤버클래스(C)에서는 바깥클래스의 정적필드와 메소드에만 접근 할 수 있고 인스턴스필드와 메소드에는 접근 불가능하다.

로컬클래스에서 사용제한

  • 메소드의 매개변수나 로컬변수를 로컬클래스에서 사용 할 때 제한이 있다.
  • 로컬클래스의 객체는 메소드실행이 종료되면 없어지지만, 종료되어도 계속 실행상태로 종료되게 할 수도 있다. (ex. 로컬스레드객체 이용시)
    • 메소드를 실행하는 스레드와 다르므로 메소드가 종료된 후에도 로컬스레드객체는 실행상태로 존재 할 수 있다.
  • 자바는 이문제를 해결하기 위해 로컬클래스에서 사용하는 매개변수나 로컬변수의 값을 로컬클래스 내부에 복사해두고 사용한다. 매개변수나 로컬변수가 수정되어 값이 변경되면 복사해둔 값과 달라지므로 이를 해결하기위해 매개변수나 로컬변수를 final로 선언한다.
public class Outter{
	public void method1(final int arg){
		//arg = 100;
		//localVariable = 100;
		class Inner{
			public void method() {
			int result = arg + localVariable; //매개변수와 로컬변수를 로컬클래스 내부에서 사용할때 final특성을 갖고있음을 알 수 있다
			}
		}
	}
}
// 자바8 이후부터는 final 생략해서 써도 ㄱㅊ

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

  • 클래스 내부에서 this는 객체 자신의 참조이다.
  • this를 사용하면 바깥클래스의 객체참조가 아니라 중첩클래스의 객체참조
  • 중첩클래스 내부에서 this.필드, this.메소드()로 호출하면 중첩클래스의 필드와 메소드 사용됨
  • 중첩클래스 내부에서 바깥클래스의 객체 참조를 얻으려면 바깥클래스의 이름을 this 앞에 붙여주면 된다.
public class Outter {
	String field = "Outter-field";
	void method() {
		System.out.println("Outter-method");
	}
	
	class Nested {
		String field = "Nested-field";
		void method() {
			System.out.println("Nested-method");
		}
		
		void print() {
			System.out.println(this.field);
			this.method();
			System.out.println(Outter.this.field);
			Outter.this.method();
		}
	}

public class OutterExample {
	public static void main(String[] args) {
		Outter outter = new Outter();
		Outter.Nested nested = outter.new Nested();
		nested.print();
	}

}

🥕 중첩 인터페이스

  • 클래스의 멤버로 선언된 인터페이스
  • 해당 클래스와 긴밀한 관계를 갖는 구현 클래스를 만들기 위함
class A {
	[static] interface I {
		void method();
	}
}
  • 인스턴스멤버 인터페이스와 정적멤버인터페이스가 모두 가능
  • 인스턴스멤버 인터페이스는 바깥 클래스의 객체가 있어야 사용 가능
  • 정적 멤버 인터페이스는 바깥클래스 객체 없이 바깥클래스만으로 접근 가능 (이벤트 처리 목적으로 많이 사용)
버튼 클릭시 이벤트를 처리하는 객체를 받는다고 가정, 
버튼 내부에 선언된 중첩 인터페이스를 구현한 객체만 받아야한다.

public class Button {
	OnClickListener listener; // 중첩인터페이스 타입으로 필드를 선언
	
	void setOnClickListener(OnClickListener listener) {
		this.listener = listener; // 구현객체를 받아 필드에 대입
	}

	void touch() {
		listener.onClick(); 
	}
	
	static interface OnClickListener {
		void onClick(); // 버튼이벤트(touch()실행시) 발생시 인터페이스를 통해 구현객체의 
                     //메소드 호출 
	}
}

//구현클래스
public class CallListener implements Button.OnClickListener {
	@Override
	public void onClick() {
		System.out.println("전화를 겁니다");
	}

}

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

}

//이벤트처리
public class ButtonExample {
	public static void main(String[] args) {
		Button btn = new Button();
		
		btn.setOnClickListener(new CallListener());
		btn.touch();
		
		btn.setOnClickListener(new MessageListener());
		btn.touch();
	}

}

🥕 2. 익명객체

일회용객체 느낌

인터페이스였던 리모트컨트롤과 그 구현객체인 오디오 등이 있을때

오디오는 오디오라는 잌름이있음

new Remotecontrol(){
	@Override
	public void turnOn(){
	}
	@Override
	public void turnOff(){
	}
	}

이런식으로 이름이 없지만 메소드를 재정의 할 수 있다

오디오나 티비는 계속 쓸라고 클래스 정의 했는데, 위의 경우 걍 생산과 동시에 사용하고 .. control변수 사라지면 저 코드는 이제 의미없어지는거... ㄹㅇ로 한번쓰고 버림

편할라고 쓰는거임

인터페이스 Avoid method1();이 있을때,
A를 상속받는 객체르 만들수도 있지만 익명개체를 쓸수도 있다
구현받는 바디가 없으니까... 구현체가 있어야 생성이 되겠지
new A() { void method1(); } 이렇게 오버라이드해서 사용하면 된다

익명개체의 조건 : 어떤 클래스를 상속하거나 인터페이스를 구현해야한다.

  • 클래스를 선언할때 일반적으로 클래스이름과 동일한 소스파일을 생성하고 클래스를 선언한다. 한번 선언한뒤 여러곳에서 객체를 만들어 쓸때 클래스이름으로 생성자를 호출하기 쉽기 때문이인데, 클래스 이름이 없는 객체, 즉 익명객체가 존재한다.
  • 익명객체를 만들땐 조건이 있다. 클래스를 상속하거나 인터페이스를 구현해야한다.
[상속]
class 클래스이름1 extends 부모클래스 {...}
부모클래스 변수 = new 클래스이름1();
// 위와같이 상속을 처리했으나 익명객체는 클래스 이름이 없으므로
**부모클래스 변수 = new 부모클래스() {...};
// 부모클래스는 이름이 없는 자식 객체를 참조**

[구현]
class 클래스이름2 implements 인터페이스 {...}
인터페이스 변수 = new 클래스이름2();
// 마찬가지로 클래스이름이 없으므로
**인터페이스 변수 = new 인터페이스() {...}
// 이름이 없는 구현객체를 참조**

익명 자식 객체 생성

  • 부모타입의 필드 또는 변수를 선언하고 자식 객체를 초기값을 대입하는경우, 부모클래스를 상속해서 자식클래스를 선언한다. 그리고 new를 이용해 자식객체를 생성하고 부모타입 필드나 변수에 대입한다.
class Child extends Parent {  } // 부모클래스 상속한 자식클래스 선언

class A {
	Parent field = new Child(); // 필드에 자식객체 대입
	void method() {
	Parent localVar = new Child(); // 로컬변수에 자식객체 대입
	}
}
  • 자식클래스를 명시적으로 사용함으로써 어디서건 이미 선언된 자식클래스로 간단히 객체를 생성하여 사용할 수 있다 = 재사용성이 높다.
  • 자식객체가 재사용되지 않고 오로지 특정위치에서만 사용할거면 자식객체를 명시적 선언 X, 익명자식객체를 생성해야한다.
부모클래스 [필드|변수] = new 부모클래스(매개값, ...) {
 //필드 (오버라이딩)
 //메소드 (오버라이딩)
};
  • 일반클래스와의 차이점은 생성자를 선언 할 수 없다!

0개의 댓글