본문 바로가기
Java

Java 중급 1 - 중첩클래스, 내부 클래스(2)

by KongJiHoon 2025. 4. 13.

1. 내부 클래스

  • 자바에서는 클래스 안에 또 다른 클래스를 선언할 수 있다.
    이러한 클래스들을 중첩 클래스(Nested Class) 라고 부른다.
  • 그 중에서도 static 키워드 없이 선언된 클래스는 내부 클래스(Inner Class) 라고 부른다.
  • 내부 클래스는 바깥 클래스의 인스턴스에 소속되며, 바깥 클래스의 모든 멤버(필드, 메서드) 에 접근 가능하다.

2. 내부 클래스 사용 이유

  • 캡슐화
    • 외부에서 사용할 필요가 없는 클래스는 감춰야한다.
    • 클래스 설계의 기본 원칙 중 하나는 정보 은닉(information hiding) 이다.
      즉, 외부에서 직접 접근할 필요가 없는 구현 세부사항은 가능한 한 감춰야 한다. 
    • 내부 클래스는 외부에 공개되지 않고, 바깥 클래스 내부에서만 쓰이도록 설계할 수 있기 때문에
      불필요한 클래스를 외부에 노출하지 않아도 된다.
  • 논리적 그룹화
    • 특정 기능에서만 사용하는 클래스를 그 안에 정의하면 코드 가독성이 올라간다.
    • 기능적으로 밀접한 관계인 클래스는 함께 묶는 것이 유지보수에 더 유리하다.
    • 내부 클래스는 특정 기능에서만 사용되는 클래스가 있을 때, 해당 기능 내부에 선언함으로써 관련 로직을 논리적으로 묶을 수 있다.

 

3. 예제 InnerClass

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);     // 1
            System.out.println(outInstanceValue);       // 2
            System.out.println(outClassValue);          // 3
        }
    }
}

 

public class InnerOuterMain {
    public static void main(String[] args) {
        InnerOuter outer = new InnerOuter();
        InnerOuter.Inner inner = outer.new Inner();
        inner.print();
    }
}

 

  • 내부 클래스는 바깥 클래스 인스턴스.내부 클래스 생성() 방식으로 생성해야 한다.
  • 바깥 클래스의 모든 멤버(static, non-static)에 접근 가능하다.
  • 내부 클래스는 바깥 클래스 인스턴스에 소속되어 동작한다.

 

4. 내부 클래스 활용

 

리펙토링 전

public class Engine {
    private Car car;

    public Engine(Car car) {
        this.car = car;
    }

    public void start() {
        System.out.println(car.getChargeLevel());
        System.out.println(car.getModel() + "의 엔진을 구동합니다.");
    }
}

 

public class Car {
    private String model;
    private int chargeLevel;
    private Engine engine;

    public Car(String model, int chargeLevel) {
        this.model = model;
        this.chargeLevel = chargeLevel;
        this.engine = new Engine(this);
    }

    public String getModel() { return model; }
    public int getChargeLevel() { return chargeLevel; }

    public void start() {
        engine.start();
        System.out.println(model + " 시작 완료");
    }
}

 

  • Engine은 Car에서만 사용하는데도 외부 클래스처럼 보임
  • getModel(), getChargeLevel() 같은 메서드를 외부에 공개해야 함 → 캡슐화 깨짐

 

리펙토링

public class Car {
    private String model;
    private int chargeLevel;
    private Engine engine;

    public Car(String model, int chargeLevel) {
        this.model = model;
        this.chargeLevel = chargeLevel;
        this.engine = new Engine();
    }

    public void start() {
        engine.start();
        System.out.println(model + " 시작 완료");
    }

    private class Engine {
        public void start() {
            System.out.println("충전 레벨 확인: " + chargeLevel);
            System.out.println(model + "의 엔진을 구동합니다.");
        }
    }
}

 

 

 

  • Engine은 더 이상 외부에 노출되지 않음
  • Car 내부 정보에 직접 접근 가능 → getter 없이도 협력 가능
  • 코드 응집도 상승, 외부 노출 최소화
구분 정적 중첩 클래스 내부 클래스
static O X
바깥 인스턴스 필요 X O
바깥 멤버 접근 static만 전부 가능
생성 방법 new Outer.Nested() outer.new Inner()
목적 논리적 분리 논리적 + 구조적 소속