자바 고급(JAVA)

내부 클래스(Inner Class)

beginner-development 2026. 5. 15. 22:31

1.  내부 클래스란?

클래스 내부에 선언된 또 다른 클래스로, 외부 클래스의 멤버(필드/메서드)에 자유롭게 접근할 수 있으며, 밀접하게 관련된 기능을 함께 묶어 설계할 수 있도록 도와준다. 또한, 내부 클래스를 활용하면, 코드의 응집도를 높이고, 캡슐화를 강화할 수 있다.

1-2. 내부 클래스의 종류

구분 선언 위치 외부 클래스 접근 가능 여부 특징
1. 인스턴스 내부 클래스 외부 클래스의 멤버 위치 외부 인스턴스 변수 접근 가능 외부 클래스와 강한 결합
2. 정적(static) 내부 클래스 외부 클래스의 멤버 위치 외부의 static 멤버만 접근 가능 static 키워드 사용, 독립성 높음
3.지역 내부 클래스 외부 클래스의 메서드 또는 초기화 블록 내부 final 또는 effectively final 지역 변수 접근 가능 메서드 내에서만 사용
4. 익명 내부 클래스 클래스 정의와 인스턴스 생성을  동시에 외부 클래스 멤버 접근 가능 이름 없는 일회성 클래스

1-3. 사용하는 이유

1-3-1. 외부 클래스와 밀접한 기능을 묶기 위해

두 클래스가 강하게 연관된 관계라면 외부 클래스 내부에 클래스를 선언하는 것이 자연스럽다.

//예) 버튼 클릭 이벤트 리스너 등은 버튼 클래스 외부에서는 사용되지 않아 내부에 정의하는 것이 이상적
class Button {
    class ClickListener {
        void onClick() {
            System.out.println("버튼이 클릭됨");
        }
    }
    
    ClickListener event = new ClickListener();
    // 버튼 클릭시
    if(버튼클릭시) {
	    event.onClick();
    }
}

1-3-2. 캡슐화(정보 은닉) 강화

내부 클래스를 private, protected로 선언하면 외부에서 접근이 불가 → 캡슐화 목적에 부합

class Outer {
    private class HiddenHelper {//내부 구현을 숨기고 외부로 노출 X
        void help() {
            System.out.println("숨겨진 내부 로직 수행");
        }
    }
}

1-3-3. 응집도 증가 및 유지보수 향상

  • 관련된 클래스를 물리적으로 하나의 파일 내부에 위치시켜 가독성과 관리성 향상
  • DTO, 이벤트 처리 등에서 자주 사용됨

2. 멤버 내부 클래스

클래스의 멤버처럼 외부 클래스의 내부에 선언된 클래스를 말하며, 인스턴스 내부 클래스(non-static)정적 내부 클래스(static)로 구분된다.

2-1. 인스턴스 내부 클래스(Instance Inner Class)

외부 클래스 객체 생성 후 사용이 가능하며, 정적 멤버를 선언할 수 없다. 또한 외부 클래스의 인스턴스 멤버와 static 멤버 모두 접근이 가능하다.

class Outer {
    private int num = 1;         // 외부 클래스의 인스턴스 변수
    private static int sNum = 2; // 외부 클래스의 static 변수

    private InClass inClass;     // 내부 클래스 자료형 변수 선언

    public Outer() {
        inClass = new InClass(); // 외부 클래스 생성자에서 내부 클래스 초기화
    }

    class InClass {
        int inNum = 10; // 내부 클래스의 인스턴스 변수

        void test() {
            System.out.println("Outer num = " + num + "(외부 클래스의 인스턴스 변수)");
            System.out.println("Outer sNum = " + sNum + "(외부 클래스의 정적 변수)");
        }
    }

    public void testClass() {
        inClass.test();
    }
}

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        System.out.println("외부 클래스를 통해 내부 클래스 메서드 호출");
        outer.testClass();
    }
}

2-2. 정적 내부 클래스(Static Inner Class)

외부 클래스의 인스턴스 없이도 생성이 가능하며, 외부 클래스의 static 멤버만 접근이 가능하다. 또한 정적 컨텍스트에서 주로 활용된다.

(※ static 멤버만 접근이 가능하다는게 중요!!!)

class Outer {
    private int num = 3;         // 외부 클래스의 인스턴스 변수
    private static int sNum = 4; // 외부 클래스의 static 변수

    void getPrint() {
        System.out.println("인스턴스 메서드");
    }

    static void getPrintStatic() {
        System.out.println("스태틱 메서드");
    }

    static class StaticInClass {
        void test() {
            System.out.println("Outer sNum = " + sNum + "(외부 클래스의 정적 변수)");
            getPrintStatic(); // static 메서드 호출 가능
            // System.out.println(num);         → 에러 (인스턴스 멤버 접근 불가)
            // getPrint();                      → 에러 (인스턴스 메서드 접근 불가)
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer.StaticInClass a = new Outer.StaticInClass(); // 외부 클래스 인스턴스 없이 사용
        a.test();
    }
}

3. 지역 내부 클래스

3-1. 정의 및 특징

3-1-1. 정의

외부 클래스의 메서드 내에 선언된 클래스로, 클래스의 멤버가 아닌 지역 변수처럼 메서드 안에서만 사용할 수 있다. 일반적으로 해당 메서드 안에서 정의 객체 생성 사용의 흐름으로 처리된다.

3-1-2. 특징

  1. 선언된 메서드 내부에서만 유효하다.
  2. 외부 클래스의 인스턴스 변수, 그리고 해당 메서드의 지역 변수에 접근할 수 있다.
  3. 단, 메서드의 지역 변수는 반드시 final이거나 effectively final(실질적으로 상수)이어야 한다.
    • ※ effectively final이란?
      • final이 없어도 변하지 않는 변수 → 테스트 코드를 만들 때 쓰인다.
// 지역 내부 클래스 예제
class Outer {
    int num = 5; // 외부 클래스의 인스턴스 변수

    void test() {
        int num2 = 6; // 지역 변수

        class LocalInClass {
            void getPrint() {
                System.out.println(num);   // 외부 클래스 인스턴스 변수 접근
                System.out.println(num2);  // 지역 변수 접근 (final 또는 effectively final이어야 함)
            }
        }

        LocalInClass localInClass = new LocalInClass(); // 지역 내부 클래스 객체 생성
        localInClass.getPrint();
    }
}

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.test(); // 지역 내부 클래스 실행
    }
}
//effectively final 예제
void exampleMethod() {
    int value = 100; // final은 생략했지만 값이 바뀌지 않음

    class Inner {
        void print() {
            System.out.println(value); // 가능: effectively final
        }
    }

    // value = 200; → 이 경우 에러! final 속성이 깨지므로 Inner에서 참조 불가
}