Java 공부 52일차(중첩 클래스, 내부 클래스란?)1편

임선구·2025년 7월 11일

몸 비틀며 Java

목록 보기
53/58

오늘의 잔디


중첩 클래스, 내부 클래스란?

다음과 같이 for문 안에 for문을 중첩하는 것을 중첩(Nested) for문이라 한다.

for (...) {
 //중첩 for문
 for (...) {
 }
}

다음과 같이 클래스 안에 클래스를 중첩해서 정의할 수 있는데, 이것을 중첩 클래스(Nested Class)라 한다.

class Outer {
 ...
 //중첩 클래스
 class Nested {
 ...
 }
}

중첩 클래스는 클래스를 정의하는 위치에 따라 다음과 같이 분류한다.

중첩 클래스의 분류


중첩 클래스는 총 4가지가 있고, 크게 2가지로 분류할 수 있다.

  • 정적 중첩 클래스
  • 내부 클래스 종류
    • 내부 클래스
    • 지역 클래스
    • 익명 클래스

중첩 클래스를 정의하는 위치는 변수의 선언 위치와 같다.
변수의 선언 위치

  • 정적 변수(클래스 변수)
  • 인스턴스 변수
  • 지역 변수

중첩 클래스의 선언 위치

  • 정적 중첩 클래스 -> 정적 변수와 같은 위치
  • 내부 클래스 -> 인스턴스 변수와 같은 위치
  • 지역 클래스 -> 지역 변수와 같은 위치
class Outer {
 ...
 //정적 중첩 클래스
 static class StaticNested {
 ...
 }
 //내부 클래스
 class Inner {
 ...
 }
}
  • 정적 중첩 클래스는 정적 변수와 같이 앞에 static 이 붙어있다.
  • 내부 클래스는 인스턴스 변수와 같이 앞에 static 이 붙어있지 않다.
class Outer {
 public void process() {
 //지역 변수
 int lcoalVar = 0;
 //지역 클래스
 class Local {...}
 Local local = new Local();
 }
}
  • 지역 클래스는 지역 변수와 같이 코드 블럭 안에서 클래스를 정의한다.
  • 참고로 익명 클래스는 지역 클래스의 특별한 버전이다. 이후에 설명한다.

다시 한번 정리해보자.

중첩 클래스는 총 4가지가 있고, 크게 2가지로 분류할 수 있다.

  • 정적 중첩 클래스
  • 내부 클래스 종류
    • 내부 클래스
    • 지역 클래스
    • 익명 클래스

여기서 정적 중첩 클래스와 내부 클래스로 분류하는 것을 확인할 수 있다.

그럼 중첩이라는 단어와 내부라는 단어는 무슨 차이가 있는 것일까?

  • 중첩(Nested): 어떤 다른 것이 내부에 위치하거나 포함되는 구조적인 관계
  • 내부(Inner): 나의 내부에 있는 나를 구성하는 요소

쉽게 이야기하면 여기서 의미하는 중첩( Nested )은 나의 안에 있지만 내것이 아닌 것을 말한다. 단순히 위치만 안에 있는 것이다. 반면에 여기서 의미하는 내부( Inner )는 나의 내부에서 나를 구성하는 요소를 말한다.
예)

  • 큰 나무 상자안에 전혀 다른 작은 나무 상자를 넣은 것은 중첩( Nested )이라 한다.
  • 나의 심장은 나의 내부( Inner )에서 나를 구성하는 요소이다.

정리하면 정적 중첩 클래스는 바깥 클래스의 안에 있지만 바깥 클래스와 관계 없는 전혀 다른 클래스를 말한다.
내부 클래스는 바깥 클래스의 내부에 있으면서 바깥 클래스를 구성하는 요소를 말한다.

여기서 의미하는 중첩( Nested )과 내부( Inner )를 분류하는 핵심은 바로 바깥 클래스 입장에서 볼 때 안에 있는 클래스가 나의 인스턴스에 소속이 되는가 되지 않는가의 차이이다.

  • 정적 중첩 클래스는 바깥 클래스와 전혀 다른 클래스이다. 따라서 바깥 클래스의 인스턴스에 소속되지 않는다.
  • 내부 클래스는 바깥 클래스를 구성하는 요소이다. 따라서 바깥 클래스의 인스턴스에 소속된다.

정리하면 내부 클래스들은 바깥 클래스의 인스턴스에 소속된다. 정적 중첩 클래스는 그렇지 않다.

정적 중첩 클래스

  • static 이 붙는다.
  • 바깥 클래스의 인스턴스에 소속되지 않는다.
    내부 클래스
  • static 이 붙지 않는다.
  • 바깥 클래스의 인스턴스에 소속된다.

내부 클래스의 종류

  • 내부 클래스(inner class): 바깥 클래스의 인스턴스의 멤버에 접근
  • 지역 클래스(local class): 내부 클래스의 특징 + 지역 변수에 접근
  • 익명 클래스(anonymous class): 지역 클래스의 특징 + 클래스의 이름이 없는 특별한 클래스

용어 정리

  • 중첩 클래스: 정적 중첩 클래스 + 내부 클래스 종류 모두 포함
  • 정적 중첩 클래스: 정적 중첩 클래스를 말함
  • 내부 클래스: 내부 클래스, 지역 클래스, 익명 클래스를 포함해서 말함

참고 - 실무 용어
실무에서는 중첩, 내부라는 단어를 명확히 구분하지 않고, 중첩 클래스 또는 내부 클래스라고 이야기한다. 왜냐하면 클래스 안에 클래스가 있는 것을 중첩 클래스라고 하기 때문이다. 그리고 내부 클래스도 중첩 클래스의 한 종류이다. 따라서 둘을 명확히 구분하지는 않는다. 엄밀하게 이야기하면 static 이 붙어있는 정적 중첩 클래스는 내부 클래스라고 하면 안된다. 하지만 대부분의 개발자들이 둘을 구분해서 말하지 않기 때문에 내부 또는 중첩 클래스라고 하면 상황과 문맥에 따라서 이해하면 된다.

중첩 클래스는 언제 사용해야 하나?

  • 내부 클래스를 포함한 모든 중첩 클래스는 특정 클래스가 다른 하나의 클래스 안에서만 사용되거나, 둘이 아주 긴밀하게 연결되어 있는 특별한 경우에만 사용해야 한다. 외부의 여러 클래스가 특정 중첩 클래스를 사용한다면 중첩 클래스로 만들면 안된다.

중첩 클래스를 사용하는 이유

  • 논리적 그룹화: 특정 클래스가 다른 하나의 클래스 안에서만 사용되는 경우 해당 클래스 안에 포함하는 것이 논리적으로 더 그룹화 된다. 패키지를 열었을 때 다른 곳에서 사용될 필요가 없는 중첩 클래스가 외부에 노출되지 않는 장점도 있다.
  • 캡슐화: 중첩 클래스는 바깥 클래스의 private 멤버에 접근할 수 있다. 이렇게 해서 둘을 긴밀하게 연결하고 불필요한 public 메서드를 제거할 수 있다. 이 부분은 말로 이해하기는 어렵기 때문에 이후에 예제를 통해서 알아보자.

정적 중첩 클래스

예제 코드를 통해 정적 중첩 클래스(static nested class)를 알아보자.

package nested.nested;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);
 }
 }
}
  • 정적 중첩 클래스는 앞에 static 이 붙는다.
  • 정적 중첩 클래스는
    • 자신의 멤버에는 당연히 접근할 수 있다.
    • 바깥 클래스의 인스턴스 멤버에는 접근할 수 없다.
    • 바깥 클래스의 클래스 멤버에는 접근할 수 있다.

참고로 NestedOuter.outClassValueoutClassValue 와 같이 줄여서 사용해도 된다. 이 경우 바깥 클래스에 있는 필드를 찾아서 사용한다.

private 접근 제어자

  • private 접근 제어자는 같은 클래스 안에 있을 때만 접근할 수 있다.
  • 중첩 클래스도 바깥 클래스와 같은 클래스 안에 있다. 따라서 중첩 클래스는 바깥 클래스의 private 접근 제어자에 접근할 수 있다.
package nested.nested;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.Nested 와 같이 바깥 클래스.중첩클래스 로 접근할 수 있다.
  • 여기서 new NestedOuter() 로 만든 바깥 클래스의 인스턴스와
    new NestedOuter.Nested() 로 만든 정적 중첩 클래스의 인스턴스는 서로 아무 관계가 없는 인스턴스이다. 단지 클래스 구조상 중첩해 두었을 뿐이다.
    • 참고로 둘이 아무런 관련이 없으므로 정적 중첩 클래스의 인스턴스만 따로 생성해도 된다.

실행 결과

1
3
nestedClass = class nested.nested.NestedOuter$Nested

중첩 클래스를 출력해보면 중첩 클래스의 이름은 NestedOuter$Nested 와 같이 바깥 클래스, $ , 중첩 클래스의 조합으로 만들어진다.

그림을 통해 코드를 분석해보자.



Nested.print() 를 살펴보자.
정적 중첩 클래스는 바깥 클래스의 정적 필드에는 접근할 수 있다. 하지만 바깥 클래스가 만든 인스턴스 필드에는 바로 접근할 수 없다. 바깥 인스턴스의 참조가 없기 때문이다.

정리
정적 중첩 클래스는 사실 다른 클래스를 그냥 중첩해 둔 것일 뿐이다! 쉽게 이야기해서 둘은 아무런 관계가 없다.
NestedOuter.outClassValue 와 같은 정적 필드에 접근하는 것은 중첩 클래스가 아니어도 어차피 클래스명.정적필드명 으로 접근할 수 있다.
쉽게 이야기해서 다음과 같이 정적 중첩 클래스를 만들지 않고, 그냥 클래스2개를 따로 만든것과 같다.

class NestedOuter {
}
class Nested {
}

이 코드와 정적 중첩 클래스의 유일한 차이는 같은 클래스에 있으니 private 접근 제어자에 접근할 수 있다는 정도이다.

profile
끝까지 가면 내가 다 이겨

0개의 댓글