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

임선구·2025년 7월 13일

몸 비틀며 Java

목록 보기
54/58

오늘의 잔디


정적 중첩 클래스의 활용

간단한 예제를 리팩토링 하면서 정적 중첩 클래스를 어떻게 활용하는지 알아보자.

정적 중첩 클래스로 리팩토링 전

package nested.nested.ex1;// Network 객체 안에서만 사용
public class NetworkMessage {
 private String content;
 public NetworkMessage(String content) {
 this.content = content;
 }
 public void print() {
 System.out.println(content);
 }
}
  • NetworkMessageNetwork 객체 안에서만 사용되는 객체이다.
package nested.nested.ex1;
public class Network {
 public void sendMessage(String text) {
 NetworkMessage networkMessage = new NetworkMessage(text);
 networkMessage.print();
 }
}
  • text 를 입력 받아서 NetworkMessage 를 생성하고 출력하는 단순한 기능을 제공한다.
package nested.nested.ex1;
public class NetworkMain {
 public static void main(String[] args) {
 Network network = new Network();
 network.sendMessage("hello java");
 }
}
  • Network 를 생성하고 network.sendMessage() 를 통해 메시지를 전달한다.
  • NetworkMain 은 오직 Network 클래스만 사용한다. NetworkMessage 클래스는 전혀 사용하지 않는다.
    NetworkMessage 는 오직 Network 내부에서만 사용된다.

실행 결과

hello java

ex1 패키지를 열어보면 다음 두 클래스가 보일 것이다. ( main 은 제외)

  • Network
  • NetworkMessage

Network 관련 라이브러리를 사용하기 위해서 ex1 패키지를 열어본 개발자는 아마도 두 클래스를 모두 확인해볼 것이다. 그리고 해당 패키지를 처음 확인한 개발자는 NetworkNetworkMessage 를 둘다 사용해야 하나? 라고 생각할 것이다. NetworkMessage 에 메시지를 담아서 Network 에 전달해야 하나?와 같은 여러가지 생각을 할 것이다.
아니면 NetworkMessage 가 다른 여러 클래스에서 사용되겠구나 라고 생각할 것이다.
두 클래스의 코드를 모두 확인하고 나서야 아~ Network 클래스만 사용하면 되는구나, NetworkMessage 는 단순히 Network 안에서만 사용되는구나 라고 이해할 수 있다.

정적 중첩 클래스로 리팩토링 후

package nested.nested.ex2;
public class Network {
 public void sendMessage(String text) {
 NetworkMessage networkMessage = new NetworkMessage(text);
 networkMessage.print();
 }
 private static class NetworkMessage {
 private String content;
 public NetworkMessage(String content) {
 this.content = content;
 } public void print() {
 System.out.println(content);
 }
 }
}
  • NetworkMessage 클래스를 Network 클래스 안에 중첩해서 만들었다.
  • NetworkMessage 의 접근 제어자를 private 설정했다. 따라서 외부에서 NetworkMessage 에 접근할 수 없다.
    • 예) new Network.NetworkMessage() 처럼 접근할 수 없다.
package nested.nested.ex2;
public class NetworkMain {
 public static void main(String[] args) {
 Network network = new Network();
 network.sendMessage("hello java");
 }
}

실행 결과

hello java

ex1 패키지를 열어보면 다음 하나의 클래스가 보일 것이다. ( main 은 제외)

  • Network

Network 관련 라이브러리를 사용하기 위해서 ex2 패키지를 열어본 개발자는 해당 클래스만 확인할 것이다. 추가로 NetworkMessage 가 중첩 클래스에 private 접근 제어자로 되어 있는 것을 보고, Network 내부에서만 단독으로 사용하는 클래스라고 바로 인지할 수 있다.

중첩 클래스의 접근

나의 클래스에 포함된 중첩 클래스가 아니라 다른 곳에 있는 중첩 클래스에 접근할 때는 바깥클래스.중첩클래스 로 접근해야 한다.

NestedOuter.Nested nested = new NestedOuter.Nested();

나의 클래스에 포함된 중첩 클래스에 접근할 때는 바깥 클래스 이름을 적지 않아도 된다.

public class Network {
 public void sendMessage(String text) {
 NetworkMessage networkMessage = new NetworkMessage(text);
 }
 private static class NetworkMessage {...}
}

중첩 클래스(내부 클래스 포함)는 그 용도가 자신이 소속된 바깥 클래스 안에서 사용되는 것이다. 따라서 자신이 소속된 바깥 클래스가 아닌 외부에서 생성하고 사용하고 있다면, 이미 중첩 클래스의 용도에 맞지 않을 수 있다. 이때는 중첩 클래스를 밖으로 빼는 것이 더 나은 선택이다.

내부 클래스

정적 중첩 클래스는 바깥 클래스와 서로 관계가 없다. 하지만 내부 클래스는 바깥 클래스의 인스턴스를 이루는 요소가
된다. 쉽게 이야기해서 내부 클래스는 바깥 클래스의 인스턴스에 소속된다.

정적 중첩 클래스

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

예제 코드를 통해 내부 클래스를 알아보자.

package nested.inner;
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);
 }
 }
}
  • 내부 클래스는 앞에 static 이 붙지 않는다. 쉽게 이야기해서 인스턴스 멤버가 된다.
  • 내부 클래스는
    • 자신의 멤버에는 당연히 접근할 수 있다.
    • 바깥 클래스의 인스턴스 멤버에 접근할 수 있다.
    • 바깥 클래스의 클래스 멤버에 접근할 수 있다.

private 접근 제어자

  • private 접근 제어자는 같은 클래스 안에 있을 때만 접근할 수 있다.
  • 내부 클래스도 바깥 클래스와 같은 클래스 안에 있다. 따라서 내부 클래스는 바깥 클래스의 private 접근 제어자에 접근할 수 있다.
package nested.inner;
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());
 }
}
  • 내부 클래스는 바깥 클래스의 인스턴스에 소속된다. 따라서 바깥 클래스의 인스턴스 정보를 알아야 생성할 수 있다.
  • 내부 클래스는 바깥클래스의 인스턴스 참조.new 내부클래스() 로 생성할 수 있다.
    • 내부 클래스는 바깥 클래스의 인스턴스에 소속되어야 한다. 따라서 내부 클래스를 생성할 때, 바깥 클래스의
      인스턴스 참조가 필요하다.
    • outer.new Inner() 에서 outer 는 바깥 클래스의 인스턴스 참조를 가진다.
  • outer.new Inner() 로 생성한 내부 클래스는 개념상 바깥 클래스의 인스턴스 내부에 생성된다.
  • 따라서 바깥 클래스의 인스턴스를 먼저 생성해야 내부 클래스의 인스턴스를 생성할 수 있다.

실행 결과

1
2
3
innerClass = class nested.inner.InnerOuter$Inner

개념 - 내부 클래스의 생성

  • 개념상 바깥 클래스의 인스턴스 내부에서 내부 클래스의 인스턴스가 생성된다.
  • 따라서 내부 인스턴스는 바깥 인스턴스를 알기 때문에 바깥 인스턴스의 멤버에 접근할 수 있다.

실제 - 내부 클래스의 생성

  • 실제로 내부 인스턴스가 바깥 인스턴스 안에 생성되는 것은 아니다. 하지만 개념상 인스턴스 안에 생성된다고 이해하면 충분하다.
  • 실제로는 내부 인스턴스는 바깥 인스턴스의 참조를 보관한다. 이 참조를 통해 바깥 인스턴스의 멤버에 접근할 수 있다.

정리
정적 중첩 클래스와 다르게 내부 클래스는 바깥 인스턴스에 소속된다.
중첩이라는 단어와 내부라는 단어의 차이를 다시 한번 정리해보자.

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

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

정적 중첩 클래스는 다른 클래스를 그냥 중첩해 둔 것일 뿐이다. 쉽게 이야기해서 둘은 아무런 관계가 없다. 반면에 내부 클래스는 바깥 클래스의 인스턴스 내부에서 구성 요소로 사용된다.

profile
끝까지 가면 내가 다 이겨

0개의 댓글