7. 중첩 클래스, 내부 클래스1
Last updated
Last updated
정적 중첩 클래스
내부 클래스 종류
내부 클래스
지역 클래스
익명 클래스
변수의 선언 위치
정적 변수(클래스 변수)
인스턴스 변수
지역 변수
중첩 클래스의 선언 위치
정적 중첩 클래스 -> 정적 변수와 같은 위치
내부 클래스 -> 인스턴스 변수와 같은 위치
지역 클래스 -> 지역변수와 같은 위치
중첩(Nested) : 어떤 다른 것이 내부에 위치하거나, 포함되는 구조적인 관계
구조적인 의미
static
이 붙는다.
내부(Inner) : 나의 내부에 있는 나를 구성하는 요소
실제적인 의미 (인스턴스)
바깥 클래스의 인스턴스에 소속
내부 클래스를 포함한 모든 중첩 클래스는 특정 클래스가 다른 하나의 클래스 안에서만 사용되거나, 둘이 아주 긴밀하게 연결되어 있는 특별한 경우에만 사용해야 한다.
외부의 여러 클래스가 특정 중첩 클래스를 사용한다면 중첩 클래스로 만들면 안된다. (캡슐화 깨짐..)
논리적 그룹화 : 특정 클래스가 다른 하나의 클래스 안에서 사용되는 경우 해당 클래스 안에 포함하는 것이 논리적으로 더 그룹화 된다. 패키지를 열었을 때 다른 곳에서 사용될 필요가 없는 중첩 클래스가 외부에 노출되지 않는 장점도 있다.
캡슐화 : 중첩 클래스는 바깥 클래스의 private
멤버에 접근할 수 있다. 이렇게 해서 둘을 긴밀하게 연결하고 불필요한 public
메서드를 제거할 수 있다.
정적 중첩 클래스는 앞에 static
이 붙는다.
정적 중첩 클래스는
자신의 멤버에는 당연하게 접근할 수 있다.
바깥 클래스의 인스턴스 멤버에는 접근할 수 없다..
바깥 클래스의 클래스 멤버에는 접근할 수 있다.
정적 중첩 클래스는 new 바깥클래스.중첩클래스()
로 생성할 수 있다.
정적 변수/메서드와는 달리 정적 중첩 클래스는 힙 영역에 인스턴스를 만든 후 사용된다.
정적 변수/메서드 철학
공통된 속성, 행동 공유
메서드 영역에 정적 변수/메서드가 저장된다.
정적 중첩 클래스 철학
바깥 클래스 인스턴스와 연관이 없다.
메서드 영역에 정적 중첩 클래스의 설계도가 저장
힙 영역에 실제 인스턴스 저장
중첩 클래스는 바깥 클래스.중첩클래스
로 접근할 수 있다.
실제로 바깥 클래스와 중첩 클래스는 메모리적으로 관련이 없는 클래스이다. 단지 구조적으로 중첩해두었을 뿐이다.
때문에, 정적 중첩 클래스의 인스턴스만 따로 생성해도 된다.
메모리적으로 관련은 없지만, 객체지향 관점으로 클래스 사이 관련이 있는 경우 사용한다.
그림을 통해서 코드를 분석해보자
정적 중첩 클래스는 사실 다른 클래스를 중첩해 둔 것일 뿐이다!
쉽게 이야기해서 그냥 클래스 2개를 따로 만든것과 같다. 구조적으로 중첩되어 있을 뿐!
내부 클래스는 앞에 static
이 붙지 않는다. 쉽게 이야기해서 인스턴스 멤버가 된다.
내부 클래스는
자신의 멤버에는 당연히 접근할 수 있다.
바깥 클래스의 인스턴스 멤버에 접근할 수 있다.
바깥 클래스의 클래스 멤버에 접근할 수 있다.
내부 클래스는 바깥 클래스의 인스턴스에 소속된다. 따라서 바깥 클래스의 인스턴스 정보를 알아야 생성할 수 있다.
내부 클래스는 바깥클래스의 인스턴스 참조.new 내부클래스()
로 생성할 수 있다.
내부 클래스는 바깥 클래스의 인스턴스에 소속되어야 한다.
따라서 내부 클래스를 생성할 때, 바깥 클래스의 인스턴스 참조가 필요하다.
개념상 바깥 클래스의 인스턴스 내부에서 내부 클래스의 인스턴스가 생성된다.
따라서 내부 인스턴스는 바깥 클래스를 알기 때문에, 바깥 인스턴스의 멤버에 접근할 수 있다.
실제로 내부 인스턴스가 바깥 인스턴스를 안에 생성되는 것은 아니다. 하지만 개념상 인스턴스 안에 생성된다고 이해하면 충분하다.
실제로는 내부 인스턴스는 바깥 인스턴스의 참조를 보관한다. 이 참조를 통해서 바깥 인스턴스의 멤버에 접근할 수 있다.
캡슐화를 위해 내부 클래스를 쓴다면, 실제 인스턴스와 연관 있는 일반 내부 클래스(Inner Class)를 쓰면 되지, 왜 굳이 외부 인스턴스와 무관한 정적 중첩 클래스(static nested class)를 써야 할까?"
내부 클래스는 컴파일 시 자동으로 외부 클래스의 참조(this$0
)를 갖는다
이건 때때로 GC가 외부 클래스 인스턴스를 회수하지 못하게 만들 수 있음
특히 콜백 구조나 멀티스레딩 상황에서는 의도치 않은 메모리 누수(leak)로 이어질 수 있음
정적 중첩 클래스는 아예 외부 인스턴스와 끊어진 독립 클래스니까 이런 문제가 없음
Map.Entry
처럼 단순히 관련된 타입을 내부적으로 묶어놓고 싶은 경우
외부 Map
인스턴스를 꼭 참조할 필요 없음
→ 이럴 때 정적 중첩 클래스가 더 가볍고 안전한 선택
누군가 코드를 봤을 때 static class Inner
를 보면
→ "아, 이 클래스는 외부 인스턴스에 의존하지 않는구나" 라고 한눈에 알 수 있음
설계 의도를 명확히 보여줌 → 유지보수성 향상
ClickListener
는 항상 Button
인스턴스의 상태(enabled 등)에 의존
외부 객체가 사라질 일이 없고, 연결된 관계 유지가 필수적 → 내부 클래스 적합
Builder
는 외부 클래스와 독립적으로 존재할 수 있음
필요할 때만 외부 클래스 객체를 만들므로 GC 부담 없음
내부 클래스는 ‘논리적 포함’과 ‘실제 인스턴스 참조’가 동시에 필요한 경우에만 써야한다.
그 외에는 정적 중첩 클래스를 사용하는 게 메모리, 유지보수, 성능 면에서 훨씬 유리하다.
바깥 클래스에 대한 레퍼런스 정보를 가지지 않기 때문에, GC, 클래스 로딩, CPU 캐시 등 여러 면에서 성능적으로 더 효율적이다.