# 9. 중첩클래스
# 9.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)이 별도로 생성된다. 멤버 클래스일 경우 바이트코드 파일의 이름은 다음과 같이 결정된다
A (바깥클래스) $ B (멤버클래스) .class
- 로컬 클래스일 경우에는 다음과 같이 $1 이 포함된 바이트코드 파일이 생성된다
A(바깥클래스) $1 B(로컬클래스) .class
# 9.2 인스턴스 멤버 클래스
[public] class A {
[public|private] class B { // 인스턴스 멤버 클래스
}
}
구분 | 접근범위 |
---|---|
public class B {} | 다른 패키지에서 B클래스를 사용할 수 있다 |
class B {} | 같은 패키지에서만 B클래스를 사용할 수 있다 |
private class B{} | A 클래스 내부에서만 B클래스를 사용할 수 있다 |
- 인스턴스 멤버 클래스B 는 주로 A 클래스 내부에서 사용되므로 private 접근 제한을 갖는 것이 일반적이다
- B 객체는 A 클래스 내부 어디에서나 생성할 수는 없고, 인스턴스 필드값, 생성자, 인스턴스 메소드에서 생성할 수 있다. A 객체가 있어야 B 객체도 있기 때문
public class A {
class B {} // 인스턴스 멤버 클래스
B field = new B(); // 인스턴스 필드 값으로 B 객체 대임
A () {
B b = new B(); // 생성자
}
void method() {
B b = new B(); // 인스턴스 메소드
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
B 객체를 A 클래스 외부에서 생성하려면 default 또는 public 접근 제한을 가져야 하고, A 객체르 먼저 생성한 다음 B 객체를 생성해야 한다.
A a = new A(); A.b b = a.new B();
인스턴스 멤버 클래스 B 내부에는 일반 클래스와 같이 필드, 생성자, 메소드 선언이 올 수 있다. 정적 필드와 정적 메소드는 Java 17부터 선언 가능
# 9.3 정적 멤버 클래스
- 정적 멤버 클래스 : static 키워드와 함께 A 클래스의 멤버로 선언된 B 클래스
[public] class A {
[public | private] static class B { // 정적 멤버 클래스
}
}
- 정적 멤버 클래스 B 는 A 클래스 내부에서 사용되기도 하지만 A 클래스 외부에서 A와 함께 사용되는 경우가 많기 때문에 주로 default 또는 public 접근 제한을 가진다.
- 정적 멤버 클래스 B 는 A 클래스 내부 어디든 객체를 생성할 수 있다
- A 클래스 외부에서 B 객체를 생성하려면 A 객체 생성 없이 A 클래스로 접근해서 B 객체를 생성할 수 있다.
A.B b = new A.B();
- 정적 멤버 클래스 B 내부에는 일반 클래스와 같이 필드, 생성자, 메소드 선언이 올 수 있다.
# 9.4 로컬 클래스
- 로컬(local) 클래스 : 생성자 또는 메소드 내부에서 선언된 클래스
[public] class A {
// 생성자
public A() {
class B {} // 로컬 클래스
B b = new B(); // 로컬 객체 생성
}
//메소드
public void method() {
class B {} // 로컬 클래스
B b = new B(); // 로컬 객체 생성
}
}
- 로컬 클래스는 생성자와 메소드가 실행될 동안에만 객체를 생성할 수 있다.
- 로컬 클래스 내부에는 일반 클래스와 같이 필드, 생성자, 메소드 선언이 올 수 있다. 정적 필드와 정적 메소드는 Java 17 부터 선언할 수 있다.
- 로컬 클래스 내 로컬 변수(생성자 또는 메소드의 매개변수 또는 내부에서 선언된 변수)
- 로컬 변수는 final 특성을 갖게 되므로 값을 읽을 수만 있고 수정할 수 없게 된다.
- 로컬 클래스 내부에서 값을 변경하지 못하도록 제한하기 때문.
- Java8 이후부터는 명시적으로 final 키워드를 붙이지 않아도 되지만, 로컬 변수에 final 키워드를 추가해서 final 변수임을 명확히 할 수도 있다.
- Java7 이전에는 final 키워드를 반드시 붙여야 했다.
# 9.5 바깥 멤버 접근
- 중첩 클래스는 바깥 클래스의 멤버(필드, 메소드)에 접근할 수 있다. 중첩 클래스가 어떻게 선언되었냐에 따라 접근 제한이 있을 수 있음.
# 바깥 클래스의 멤버 접근 제한
구분 | 바깥 클래스의 사용 가능한 멤버 |
---|---|
인스턴스 멤버 클래스 | 바깥 클래스의 모든 필드와 메소드 |
정적 멤버 클래스 | 바깥 클래스의 정적 필드와 정적 메소드 |
# 바깥 클래스의 객체 접근
중첩 클래스 내부에서 this 는 해당 중첩 클래스의 객체를 말한다.
바깥 클래스의 객체를 얻으려면 바깥클래스 이름에 this를 붙여준다
바깥클래스이름.this -> 바깥객체
# 9.6 중첩 인터페이스
중첩 인터페이스 : 클래스의 멤버로 선언된 인터페이스. 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해서다.
class A { [public|private] [static] interface B { // 중첩 인터페이스 //상수필드 //추상 메소드 //디폴트 메소드 //정적 메소드 } }
public class Button {
public static interface ClickListener { // 정적 멤버 인터페이스
void onClick(); // 추상 메소드
}
private ClickListener clickListner; // 필드
public void setClickListner(ClickListener clickListener) { // 메소드
this.clickListener = clickListener;
}
public void click() {
this.clickListner.onClick(); // 버튼이 클릭되었을때 실행하는 메소드 선언
}
}
public class ButtonExample {
public static void main(String[] args){
Button btnOk = new Button();
// Ok 버튼 클릭 이벤트를 처리할 ClickListner 구현 클래스(로컬 클래스)
class OkListener implements Button.ClickListner {
@Override
public void onClick() {
system.out.println("ok 버튼을 클릭했습니다");
}
}
btnOk.setClickListner(new OkListner());
btnOk.click();
}
}
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
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
# 9.7 익명 객체
- 익명(anonymous) 객체 : 이름이 없는 객체. 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성할 수 있다는 장점이 있다. 익명 객체는 필드값, 로컬 변수값, 매개변수값으로 주로 사용된다.
- 익명 객체는 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수 있다.
- 익명 자식 객체 : 클래스를 상속해서 만듦.
- 익명 구현 객체 : 인터페이스를 구현해서 만듦.
# 익명 자식 객체
- 부모 클래스를 상속받아 생성
- 부모타입의 필드, 로컬변수, 매개변수의 값으로 대입할 수 있다.
new 부모생성자(매개값,...) {
//필드
//메소드
}
- 중괄호 블록 안의 필드와 메소드는 익명 자식 객체가 가져야할 멤버로, 중괄호 블록 안에서만 사용할 수 있다.
- 익명 자식 객체는 부모 타입에 대입되므로 부모 타입에 선언된 멤버만 접근할 수 있기 때문
- 중괄호 블록 안에는 주로 부모 메소드를 재정의 하는 코드가 온다.
public class Tire {
public void roll() {
System.out.println("일반 타이어가 굴러갑니다");
}
}
public class Car {
// 필드에 Tire 객체 타입
private Tire tire = new Tire();
// 필드에 익명 자식 객체 대입
private Tire tire2 = new Tire() {
@Override
public void roll() {
System.out.println("익명 자식 Tire에 객체 1 이 굴러갑니다");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 익명 자식 객체가 부모 타입에 대입되면 부모 메소드 roll()을 호출할 경우, 재 정의된 익명 자식 객체의 roll() 메소드가 실행되는 것을 볼 수 있다 (다형성)
# 익명 구현 객체
- 인터페이스를 구현해서 생성. 이렇게 생성된 객체는 인터페이스 타입의 필드, 로컬변수, 매개변수의 값으로 대입할 수 있다.
new 인터페이스() {
//필드
//메소드
}
- 중괄호 블록 안의 필드와 메소드는 익명 구현 객체가 가져야 할 멤버로, 중괄호 블록 안에서만 사용할 수 있다.
- 익명 구현 객체는 인터페이스 타입에 대입되르모 인터페이스 타입에 선언된 멤버만 접근할 수 있기 때문.
- 중괄호 블록 안에는 주로 인터페이스의 추상 메소드를 재정의하는 코드가 온다.
public interface RemoteControl {
//추상메소드
void turnOn();
void turnOff();
}
public class Home {
// 필드에 익명 구현 객체 대입
private RemoteControl rc = new RemoteControl(){
@Override
public void turnOn(){
System.out.println("Tv를 켭니다");
}
@Override
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 Class HomeExample {
public static void main(String[] args){
Home home = new Home();
// 익명 구현객체가 대입된 매개변수 사용
home.use3(new RemoteControl() {
@Override
public void turnOn() {
System.out.println("난방을 켭니다");
}
@Override
public void turnOff(){
System.out.println("난방을 끕니다");
}
})
}
}
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class Button {
public static interface ClickListener { // 정적 멤버 인터페이스
void onClick(); // 추상 메소드
}
private ClickListener clickListner; // 필드
public void setClickListner(ClickListener clickListener) { // 메소드
this.clickListener = clickListener;
}
public void click() {
this.clickListner.onClick(); // 버튼이 클릭되었을때 실행하는 메소드 선언
}
}
public class ButtonExample {
public static void main(String[] args){
Button btnOk = new Button();
// Ok 버튼 클릭 이벤트를 처리할 ClickListner 구현 클래스(로컬 클래스)
class OkListener implements Button.ClickListner {
@Override
public void onClick() {
system.out.println("ok 버튼을 클릭했습니다");
}
}
btnOk.setClickListner(new OkListner());
btnOk.click();
Button btnCancel = new Button();
btnCancle.setClickListner(new Button.ClickListner(){
@Override
public void onClick() {
System.out.println("Cancle 버튼을 클릭했습니다");
}
});
btnCancel.click();
}
}
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
29
30
31
32
33
34
35
36
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
29
30
31
32
33
34
35
36
← 8. 인터페이스