[Java] 중첩 클래스, 내부 클래스
19. 중첩 클래스, 내부 클래스
중첩 클래스의 분류
- 정적 중첩 클래스 : static 사용, 바깥 클래스의 인스턴스에 소속 X
- 내부 클래스 종류 : static 사용 X, 바깥 클래스의 인스턴스에 소속 O
- 내부 클래스 : 바깥 클래스의 인스턴스의 멤버에 접근한다.
- 지역 클래스 : 코드 블럭 내에서 클래스를 정의한다.
- 익명 클래스 : 지역 클래스의 특징 + 클래스의 이름이 없는 특별한 클래스
중첩(Nested) vs. 내부(Inner)
- 중첩(Nested) : 어떤 다른 것이 내부에 위치하거나 포함되는 구조적 관계. 단순히 위치만 안에 있는 것이다.
- 바깥 클래스와 관계 없는 전혀 다른 클래스
- 내부(Inner) : 나의 내부에서 나를 구성하는 요소
- 바깥 클래스의 내부에 있으면서 바깥 클래스를 구성하는 요소
- 바깥 클래스의 내부에 있으면서 바깥 클래스를 구성하는 요소
중첩 클래스 사용 이유
- 중첩 클래스는 특정 클래스가 다른 하나의 클래스 안에서만 사용되거나, 둘이 아주 긴밀하게 연결되어 있는 특별한 경우에만 사용한다.
- 외부 여러곳에서 특정 클래스를 사용한다면 중첩 클래스로 사용할 수 없다.
- 특정 클래스가 다른 하나의 클래스 안에서만 사용될 때 해당 클래스 안에 포함되는 게 논리적으로 더 그룹화된다.
- 캡슐화 - 중첩 클래스는 바깥 클래스의 private 멤버에 접근 할 수 있다. 중첩 클래스를 사용하면 불필요한 public 메서드를 제거 가능하다.
정적 중첩 클래스
- 중첩 클래스
- static 사용
- 바깥 클래스의 인스턴스 멤버에는 접근할 수 없다.
- 바깥 클래스의 클래스 멤버에는 접근할 수 있다.
- private 접근 제어자
- private 접근 제어자는 같은 클래스 안에 있을 때만 접근 가능
- 중첩 클래스는 바깥 클래스의 private 접근 제어자에 접근 할 수 있다.
- 정적 중첩 클래스는
new 바깥클래스.중첩클래스()
로 생성할 수 있다.
public class NestedOuter {
private static int outClassValue = 3; // 클래스 멤버
private int outInstanceValue = 2; // 인스턴스 멤버
static class Nested {
private int nestedInstanceValue = 1;
public void print() {
// 자신의 멤버에 접근
System.out.println(nestedInstanceValue);
// 바깥 클래스의 인스턴스 멤버에는 접근할 수 없다.
// System.out.println(outInstanceValue);
// 바깥 클래스의 클래스 멤버에는 접근할 수 있다. private도 접근 가능
System.out.println(NestedOuter.outClassValue);
}
}
}
public class NestedOuterMain {
public static void main(String[] args) {
NestedOuter outer = new NestedOuter();
NestedOuter.Nested nested = new NestedOuter.Nested();
nested.print();
System.out.println("nestedClass = " + nested.getClass());
}
}
new NestedOuter()
와new NestedOuter.Nested()
로 만든 정적 중첩 클래스의 인스턴스는 서로 관계가 없는 인스턴스이다.- 정적 중첩 클래스는 다른 클래스를 그냥 중첩해둔 것
- 나의 클래스가 포함된 중첩 클래스가 아닌 다른 곳에 있는 중첩 클래스에 접근 할 때는
바깥클래스.중첩클래스
로 접근한다.NestedOuter.Nested nested = new NestedOuter.Nested();
- 나의 클래스에 포함된 중첩 클래스에 접근할 때는 바깥 클래스 이름을 적지 않는다.
public class Network { public void sendMessage(String text) { NetworkMessage networkMessage = new NetworkMessage(text); } private static class NetworkMessage {...} }
내부 클래스
- 내부 클래스
- static이 붙지 않는다.
- 바깥 클래스의 인스턴스에 소속된다.
public class InnerOuter { private static int outClassValue = 3; //클래스 멤버 private int outInstanceValue = 2; //인스턴스 멤버 class Inner { private int innerInstanceValue = 1; // 지역 멤버 public void print() { // 자신의 멤버에 접근 System.out.println(innerInstanceValue); // 외부 클래스의 인스턴스 멤버에 접근 가능, private도 접근 가능 System.out.println(outInstanceValue); // 외부 클래스의 멤버에는 접근 가능. private도 접근 가능 System.out.println(InnerOuter.outClassValue); } } }
- 바깥 클래스의 인스턴스 멤버와 클래스 멤버에 접근할 수 있다.
- private 접근 제어자
- private 접근 제어자는 같은 클래스 안에 있을 때만 접근 가능
- 내부 클래스는 바깥 클래스와 같은 클래스 안에 있어서 바깥 클래스의 private 접근 제어자에 접근 할 수 있다.
- 내부 클래스는 바깥 클래스의 인스턴스 정보를 알아야 생성 가능
new 바깥클래스의 인스턴스 참조.내부클래스()
public class InnerOuterMain { public static void main(String[] args) { InnerOuter outer = new InnerOuter(); InnerOuter.Inner inner = outer.new Inner(); inner.print(); System.out.println("innerClass = " + inner.getClass()); } }
outer.new Inner()
에서 outer는 바깥 클래스의 인스턴스 참조를 가진다.- 생성된 내부 클래스는 바깥 클래스의 인스턴스 내부에 생성된다.
- 바깥 클래스의 인스턴스가 먼저 생성되어야지 내부 클래스의 인스턴스를 생성할 수 있다.
- 실제로 내부 클래스가 바깥 클래스의 내부에 생성되는 것은 아니다.
내부 인스턴스는 바깥 인스턴스의 참조를 보관한다. 이 참조를 통해 바깥 인스턴스의 멤버에 접근할 수 있다.
- 내부 클래스의 생성
- 바깥 클래스에서 내부 클래스의 인스턴스를 생성할 때는 바깥 클래스의 이름을 생략할 수 있다.
new Engine();
- 내부 클래스의 인스턴스는 바깥 클래스의 인스턴스를 자동 참조한다.
- 바깥 클래스에서 내부 클래스의 인스턴스를 생성할 때는 바깥 클래스의 이름을 생략할 수 있다.
같은 이름의 바깥 변수 접근
- 섀도잉(Shadowing) : 다른 변수들을 가려서 보이지 않게 하는 것
public class ShadowingMain { public int value = 1; class Inner { public int value = 2; void go() { int value = 3; System.out.println("value = " + value); // 3 System.out.println("this.value = " + this.value); // 2 System.out.println("ShadowingMain.value = " + ShadowingMain.this.value); // 1 } } }
- 변수의 이름이 같을 때는 대부분 더 가깝거나 구체적인 것이 우선권을 가진다.
- 메서드 go()에서는 지역변수인 value가 가장 우선순위가 높다.
this.value
는 내부 클래스의 인스턴스에 접근한다.바깥클래스.this
는 바깥 클래스의 인스턴스에 접근한다.
- 변수의 이름이 같을 때는 대부분 더 가깝거나 구체적인 것이 우선권을 가진다.
김영한의 실전 자바 중급 1편을 참고하였습니다