클래스가 여러 클래스와 관계를 맺는 경우에는 독립적으로 선언하는 것이 좋으나, 특정 클래스만 관계를 맺을 경우에는 중첩 클래스로 선언하는 것이 유지보수에 도움이 되는 경우가 많습니다.
중첩 클래스란?
중첩 클래스(nested class)란 클래스 내부에 선언한 클래스를 말합니다. 중첩 클래스를 사용하면 클래스의 멤버를 쉽게 사용할 수 있습니다. 그리고 외부에는 중첩 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있습니다.
중첩 클래스의 종류
멤버 클래스
- 인스턴스 멤버 클래스
class A {
class B { ... }
}
A 객체를 생성해야만 B 객체를 생성할 수 있습니다.
- 정적 멤버 클래스
class A {
static class B { ... }
}
A 객체를 생성하지 않아도 B 객체를 생성할 수 있습니다.
로컬 클래스
class A {
void method() {
class B { ... }
}
}
method가 실행할 때만 B 객체를 생성할 수 있습니다.
중첩 클래스도 하나의 클래스이기 때문에 컴파일하면 바이트코드 파일(.class)가 별도로 생성됩니다.
- 멤버 클래스일 경우 바이트코드 파일의 이름 = A(바깥 클래스) $ B(멤버 클래스) .class
- 로컬 클래스일 경우 바이트코드 파일의 이름 = A(바깥 클래스) $1 B(로컬 클래스) .class
인스턴스 멤버 클래스에 대해
위에서도 살펴봤듯이 인스턴스 멤버는 class안에 class가 있는 구조입니다.
[public] class A {
[public | private] class B {
}
}
- public class B { } : 다른 패키지에서 B 클래스를 사용할 수 있습니다.
- class B { } : 같은 패키지에서만 B 클래스를 사용할 수 있습니다.
- private class B { } : A 클래스 내부에서만 B 클래스를 사용할 수 있습니다.
인스턴스 멤버 클래스 B는 주로 A 클래스 내부에서 사용되므로 private 접근 제한을 갖는 것이 일반적입니다.
B는 인스턴 필드값, 생성자, 인스턴스 메소드에서 생성할 수 있습니다.
public class A {
//인스턴스 멤버 클래스
class B {}
//인스턴스 필드 값으로 B 객체 대입
B field = new B();
//생성자
A() {
B b = new B();
}
//인스턴스 메소드
void method() {
B b = new B();
}
}
public static void main(String[] args) {
A a = new A();
A.B b = a.new B();
}
인스턴스 멤버 클래스 안에 정적 필드와 정적 메소드는 Java 17부터 사용 가능합니다
정적 멤버 클래스
public class A {
[public | private] static class B {
}
}
- public static class B {} : 다른 패키지에서 B 클래스를 사용할 수 있습니다.
- static class B {} : 같은 패키지에서만 B 클래스를 사용할 수 있습니다.
- private static class B {} : A 클래스 내부에서만 B 클래스를 사용할 수 있습니다.
public class A {
//인스턴스 멤버 클래스
class B {}
//인스턴스 필드 값으로 B 객체 대입
B field = new B();
//정적 필드 값으로 B 객체 대입
static B field2 = new B();
//생성자
A() {
B b = new B();
}
//인스턴스 메소드
void method1() {
B b = new B();
}
//정적 메소드
static void method2() {
B b = new B();
}
}
public static void main(String[] args) {
A a = new A();
A.B b = a.new B();
}
로컬 클래스
[public] class A {
//생성자
public A() {
//로컬 클래스 선언
class B {
//인스턴스 필드
int field1 = 1;
//정적 필드
static int field2 = 2;
B() { }
void method1() { }
static void method2() { }
}
//로컬 객체 생성
B b = new B();
}
//메소드
public void method() {
//로컬 클래스 선언
class B { }
//로컬 객체 생성
B n = new B();
}
}
생성자 또는 메소드 내부에서 다음과 같이 선언된 클래스를 로컬 클래스라고 합니다.
- 로컬 변수(생성자 또는 메소드의 매개변수 또는 내부에서 선언된 변수)를 로컬 클래스에서 사용할 경우 로컬 변수는 final 특성을 갖게 됩니다.
바깥 멤버 접근
중첩 클래스는 바깥 클래스와 긴밀한 관계를 맺으면서 바깥 클래스의 멤버에 접근할 수 있습니다.
바깥 클래스의 멤버 접근 제한
- 인스턴스 멤버 클래스 : 바깥 클래스의 모든 필드와 메소드
- 정적 멤버 클래스 : 바깥 클래스의 정적 필드와 정적 메소드
인스턴스 멤버와 정적 멤버가 접근할 수 있는 부분이 햇갈려 이것이 자바다 책의 예시를 써가면서 공부해봤습니다.
public class A {
//A의 인스턴스 필드와 메소드
int field1;
void method1() { }
//A의 정적 필드와 메소드
static int field2;
static void method() { }
//인스턴스 멤버 클래스
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(); // 사용 가능
}
}
//A의 인스턴스 메소드
void useB() {
B b = new B();
b.print();
}
}
바깥 클래스의 객체 접근
중첩 클래스 내부에서 this는 행당 중첩 클래스의 객체를 말합니다.
만약 중첩 클래스 내부에서 바깥 클래스의 객체를 얻으려먼 바깥 클래스 이름에 this를 붙여야 합니다.
바깥클래스이름.this -> 바깥객체
void print() {
this.method();
//A 객체의 필드와 메소드 사용
//바깥 객체 A 사용
A.this.method();
}
중첩 인터페이스
중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말합니다.
인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해서입니다.
class A {
[public | private] [static] interface B {
//상수 필드
//추상 메소드
//디폴트 메소드
//정적 메소드
}
}
- public : 외부에서도 사용할 수 있습니다.
- private : A 클래스 내부에서만 사용할 수 있습니다.
- default : 접근 제한자를 붙이지 않으면 같은 패키지 안에서만 사용할 수 있습니다.
- static : A 객체 없이 B 인터페이스를 사용할 수 있습니다.
중첩 인터페이스를 구현해 필드 값으로 생성하고 메소드 또한 구현할 수 있습니다.
익명 객체
익명 객체는 이름이 없는 객체를 말합니다. 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성할 수 있습니다.
- 익명 객체는 필드값, 로컬 변수값, 매개변수값을 주로 사용합니다.
- 익명 객체는 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수 있습니다.
- 클래스를 상속해서 만들 경우 익명 자신 객체라고 합니다.
- 인터페이스를 구현해서 만들 경우 익명 구현 객체라고 합니다.
익명 자신 객체
익명 자신 객체는 부모 클래스를 상속받아 다음과 같이 생성합니다.
new 부모생성자(매개값, ...) {
//필드
//메소드
}
- 중괄호 블록 안의 필드와 메소드는 익명 자식 객체가 가져야 하는 멤버이다.
- 중괄호 안에서만 사용 할 수 있습니다. (익명 자신 객체는 부모 타입에 대입되므로 부모 타입에 선언된 멤버만 접근)
- 중괄호 블록 안에는 주로 부모 메소드를 재정의하는 코드가 옵니다.
익명 자신 객체가 감이 잘 안와 이것이 자바다 책의 예시로 살펴보겠습니다.
public class Car {
//필드에 Tire 객체 대입
private Tire tire1 = new Tire();
//필드에 익명 자식 객체 대입
private Tire tire2 = new Tire() {
@Override
public void roll() {
}
};
//메소드(필드 이용)
public void run1() {
tire1.roll();
tire2.roll();
}
//메소드(로컬 변수 이용)
public void run2() {
//로컬 변수에 익명 자식 객체 대입
Tire tire = new Tire() {
@Override
public void roll() { }
};
tire.roll();
}
//메소드(매개변수 이용)
public void run3(Tire tire) {}
}
익명 구현 객체
익명 구현 객체는 인터페이스를 구현해서 다음과 같이 생성합니다.
new 인터페이스() {
//필드
//메소드
}
인터페이스 타입의 필드, 로컬변수, 매개변수 값으로 대입할 수 있습니다. 익명 자신 객체와 굉장히 비슷합니다. 다만 인터페이스만 다를 뿐입니다.
public interface RemoteControl {
//추상 메소드
void turnOn();
void turnOff();
}
public class Home {
//필드에 익명 구현 객체 대입
private RemoteControl rc = new RemoteControl() {
@Override
public void turnOn() {}
@Override
public void turnOff() {}
};
//메소드(필드 이용)
public void use1() {
rc.turnOn();
rc.turnOff();
}
//메소드(로컬 변수 이름)
public void use2() {
//로컬 변수에 익명 구현 객체 대입
RemoteControl rc = new RemoteControl() {
@Override
public void turnOn() {}
@Override
public void turnOff() {}
};
rc.turnOn();
rc.turnOff();
}
//메소드(매개변수 이용)
public void use3(RemoteControl rc) {
rc.turnOn();
rc.turnOff();
}
}
출처
이것이 자바다
https://product.kyobobook.co.kr/detail/S000061695652
이것이 자바다 | 신용권 - 교보문고
이것이 자바다 | JAVA 17 버전으로 업그레이드해서 돌아왔다! 7년 동안 꾸준히 사랑받은 자바 베스트셀러 1위, 『이것이 자바다』 개정판!『이것이 자바다』는 기본 개념에 충실한 설명으로 2015년
product.kyobobook.co.kr
'BackEnd > JAVA 공부' 카테고리의 다른 글
인터페이스에 대해(2) (1) | 2023.08.17 |
---|---|
인터페이스에 대해(1) (0) | 2023.08.16 |
추상 클래스와 봉인된 클래스 (0) | 2023.08.14 |
상속에 대해(2) (0) | 2023.08.14 |
상속에 대해(1) (0) | 2023.08.13 |