중첩 클래스
- 클래스 내부에 선언하는 클래스를 말함
- 중첩 클래스를 통해 두 클래스 멤버들이 서로 쉽게 접근할 수 있음
- 클래스 안에 클래스를 정리할 수 있어서 정리가 되어 코드의 복잡성을 줄일 수 있음
- 클래스 내부에 인터페이스를 선언하면 그것은 중첩 인터페이스임 (해당 클래스의 구현 클래스를 만들기 위해 선언)
중첩 클래스의 종류
- 멤버 클래스 : 클래스의 멤버로서 선언됨 -> 클래스나 객체가 사용 중일 때는 언제든 재사용 가능
멤버 클래스 안에서는 인스턴스 멤버 클래스(static이 붙지 않음)와 정적 멤버 클래스(static이 붙음)로 나뉜다.
- 로컬 클래스 : 생성자나 메소드 내부에 선언됨 -> 해당 메소드를 실행할 때만 사용 가능
- 정적 멤버 클래스 : 상위 객체가 인스터스화되지 않아도 접근 간으(내부에 정적 필도 혹은 메소드 선언이 가능)
바이트 코드 파일 이름
- 중첩 클래스도 클래스의 일종이므로 컴파일시 바이트 코드파일이 별도로 생성됨
- 멤버 클래스 : A$B.class
- 로컬 클래스 : A$1B.class
인스턴스 멤버 클래스
- static 키워드 없이 중첩 선언된 클래스
- 인스턴스 멤버 클래스 안에서는 인스턴스 필드와 인스턴스 메소드만 선언이 가능하다.
- 정적 필드와 정적 메소드는 선언이 불가능하다.
- A 클래스 안에서 B 클래스가 인스턴스 멤버 클래스로 선언되었을 때
A 클래스 외부에서 B 객체를 생성하기 위해서는 먼저 A 객체를 생성한 후 B 객체를 생성해야한다.
A 클래스 내부에서는 일반 클래스처럼 B 객체를 생성한다.
class A {
/** 인스턴스 멤버 클래스**/
class B {
B() {} // 생성자
int field1; // 인스턴스 필드
// static int field2; // 정적 필드 선언할 수 없음
void method1() {} // 인스턴스 메소드
// static void method2() {} // 정적 메소드 선언할 수 없음
}
}
// A 클래스 외부
A a = new A();
A.B b = a.new B();
b.field1 = 3;
b.method1();
// A 클래스 내부
class A {
class B { }
void methodA() {
B b = new B(); // A 클래스 내부에서는 B 객체 바로 선언 가능
b.field1 = 3;
b.method1();
}
}
정적 멤버 클래스
- static 키워드로 중첩 선언된 클래스
- 정적 필드와 정적 메소드, 인스턴스 필드와 인스턴스 메소드 모두 선언이 가능하다.
- A 클래스 안에서 C 클래스가 정적 멤버 클래스로 선언되었을 때
A 클래스 외부에서 C 객체를 생성하기 위해서는 먼저 A 객체를 생성할 필요가 없다.
후 B 객체를 생성해야한다. (A.B b = a.new B();)
A 클래스 내부에서는 일반 클래스처럼 B 객체를 생성한다.
class A {
/** 정적 멤버 클래스**/
static class C {
C() {} // 생성자
int field1; // 인스턴스 필드
static int field2; // 정적 필드
void method1() [] // 인스턴스 메소드
static void method2() // 정적 메소드
}
}
// A 클래스 외부
A.C c = new A.C();
c.field1 = 3; // 인스턴스 필드 사용
c.method1(); // 인스턴스 메소드 호출
A.C.field2 = 3; // 정적 필드 사용
A.C.method2(); // 정적 메소드 호출
로컬 클래스
- 메소드 내에서 선언되는 클래스로서 어차피 메소드 내부에서만 사용 가능하므로 접근제한자 및 static 을 붙일 수 없다
- 로컬 클래스 내부에서 필드와 메소드를 선언할 때 정적 필드와 정적 메소드는 선언할 수 없다.
void method() { // 메소드 내부에서 클래스 선언, 객체 생성, 사용 다 한다.
class D {
D() { }
int field1; // 인스턴스 필드
// static int field2; // 정적 필드 선언 X
void method1() { } // 인스턴스 메소드
// static void method2() { } // 정적 메소드 선언 X
}
D d = new D();
d.field1 = 3;
d.method1();
}
* 메소드 안에서 클래스를 선언하는 방법은 스레드 단원에서 배울 비동기 처리를 위한 스레드 객체를 생성할 때 사용함
중첩 클래스 예제
/** 바깥 클래스**/
public class A {
A() {System.out.println("A 객체가 생성됨");} // 생성자
/**인스턴스 멤버 클래스**/
class B {
B() {System.out.println("B 객체가 생성됨");}
int field1;
//static int field2;
void method1() {}
//static 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();
}
}
A 객체가 생성됨
B 객체가 생성됨
C 객체가 생성됨
D 객체가 생성됨
중첩 클래스의 접근 제한
- 중첩 클래스의 바깥에서 필드와 메소드에 대한 사용 제한
public class A1 {
//인스턴스 필드
B1 field1 = new B1();
C1 field2 = new C1();
// 인스턴스 메소드
void method1() {
B1 var1 = new B1();
C1 var2 = new C1();
}
// 정적 필드 초기화
// static B1 field3 = new B1();
static C1 field4 = new C1();
// 정적 메소드
static void method2() {
//B1 var1 = new B1();
C1 var2 = new C1();
}
// 인스턴스 멤버 클래스
// 인스턴스 필드와 인스턴스 메소드 모두에서 객체 생성 가능
// 그러나 정적 필드와 정적 메소드에서는 B 객체 생성 불가
class B1 {}
// 정적 멤버 클래스
// 어디서든 객체 생성 가능
static class C1 {}
}
- 멤버 클래스 내부에서 사용 제한
public class A2 {
//인스턴스 필드와 메소드
int field1;
void method1() {}
//정적 필드와 메소드
static int field2;
static void method2() {}
class B2 { // 인스턴스 멤버 클래스라서 모든 필드와 메소드에 접근 가능하다
void method() {
field1 = 10;
method1();
field2 = 10;
method2();
}
}
static class C { // 정적 멤버 클래스라서 정적 필드와 메소드만 접근이 가능하다.
void method() {
//field1 = 10;
//method1();
field2 = 10;
method2();
}
}
}
- 로컬 클래스에서 사용 제한
로컬 클래스에서는 메소드의 매개변수나 로컬 변수를 사용할 떼 Ffinal로 선언하도록 제한함
제한 하는 이유 : 로컬 스레드 객체의 경우 처럼 메소드가 종료되었는데도 계속 실행 상태일 수 있다.
final 로 선언하여 값이 변경되지 못하여 로컬 클래스 내부에서 복사해두고 사용할 수 있음
public class Outter { // 자바 8 이후 부터는 final 생략 가능 (알아서 final 특성을 지정)
public void method1(final int arg) { // 메소드 내에서
final int localVariable = 1; // 로컬 변수와 매개변수를 final로 선언
// arg = 100; // 컴파일 에러 발생
// localVariable = 100; // 컴파일 에러 발생
// 에러 내용 : The final local variable cannot be assigned.
class Inner { // 로컬 클래스
public void method() {
int result = arg + localVariable; // 로컬 클래스 내부에서 로컬 변수와 매개변수 사용
}
}
}
}
- 중첩 클래스에서 바깥 클래스 참조 얻기
this.필드; // 자기 자신의 참조
this.method;
바깥클래스명.this.필드; // 중첩 클래스 내부에서 바깥 클래스의 객체 참조
바깥클래스명.this.method;
- 중첩 클래스에서 바깥 클래스 참조 얻기 예제
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() { // 중첩 클래스 내부에서 print() 메소드 선언
// 중첩 클래스 내부에서 중첩 클래스 자신의 필드와 메소드 사용
System.out.println(this.field); // 그냥 this만 사용
this.method();
// 중첩 클래스 내부에서 바깥 클래스 필드와 메소드 사용
System.out.println(Outter.this.field); // 바깥클래스명.this 사용
Outter.this.method();
}
}
}
<실행 파일>
public class OutterExample {
public static void main(String[] args) {
Outter outter = new Outter(); // 바깥 클래스의 객체 생성
Outter.Nested nested = outter.new Nested(); // 중첩 클래스 객체 생성
nested.print(); // 중첩 클래스 메소드 선언
}
}
중첩 인터페이스
- 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해 클래스의 멤버로서 선언된 인터페이스
- 중첩 인터페이스 : 인스턴스와 정적 멤버 모두 가능하다.
- 인스턴스 멤버 인터페이스는 바깥 클래스의 객체가 있어야 사용 가능
- 정적 멤버 인터페이스는 바깥 클래스의 객체 없이 바깥 클래스만으로 바로 접근 가능
- 중첩 인터페이스 사용 예시 > 클래스 내부에 선언된 중첩 인터페이스를 구현한! 객체만 받고 싶을 때 사용
<Button 클래스 내에서 선언된 중첩 인터페이스>
class Button {
//static으로 중첩 인터페이스로 선언(이벤트 처리 목적)
static interface OnClickListener {
void onClick(); // 인터페이스 내 추상 메소드 선언
}
// 인터페이스 타입 필드
OnClickListener listener;
// 인터페이스 타입을 매개 값으로 받는 Setter 메소드 선언
void setOnClickListener(OnClickListener listener) { // 매개변수로 구현 객체가 들어옴
this.listener = listener; // 구현 객체를 받아 필드에 대입
}
// 버튼 이벤트 발생 메소드
void touch() {
listener.onClick(); // 인터페이스를 통해 구현 객체의 onClick() 메소드 호출
}
}
<구현 클래스>
import ch09_1_inner.Button.OnClickListener;
// 구현 클래스 1 - 전화
class CallListener implements OnClickListener {
@Override
public void onClick() {
System.out.println("전화를 겁니다.");
}
}
//구현 클래스 1 - 메시지
class MessageListener implements 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(); // 메시지를 보냅니다.
}
}
'👨🏫Study > JAVA' 카테고리의 다른 글
[JAVA] 10 - 1 예외 클래스 (0) | 2022.03.21 |
---|---|
[JAVA] 09 - 2 익명 객체 (1) | 2022.03.21 |
[JAVA] 08 - 2 타입 변환과 다형성 (0) | 2022.03.21 |
[JAVA] 08 - 1 인터페이스 (0) | 2022.03.21 |
[JAVA] 07 - 3 추상 클래스 (0) | 2022.03.21 |
댓글