0Pricing
Java Academy · 강의

내부 클래스와 외부 클래스 접근

외부 클래스 인스턴스에 대한 참조를 보유하는 비정적 내부 클래스를 사용합니다.

내부 클래스와 외부 클래스 접근은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

내부 클래스

비정적 내부 클래스는 자신을 감싸는 외부 클래스 인스턴스에 대한 암시적 참조를 보유합니다. 따라서 비공개 멤버를 포함한 외부 클래스의 모든 멤버에 접근할 수 있습니다.

내부 클래스 선언 및 사용

내부 클래스 인스턴스는 outer.new InnerClass()를 사용하여 외부 클래스 인스턴스를 통해서만 생성합니다.

class BankAccount {
    private double balance;
    private String owner;

    BankAccount(String owner, double initial) {
        this.owner = owner; this.balance = initial;
    }

    class Transaction {
        void deposit(double amount) {
            balance += amount; // accesses outer field directly
            System.out.println(owner + " deposited " + amount);
        }
    }
}

BankAccount acct = new BankAccount("Alice", 500);
BankAccount.Transaction tx = acct.new Transaction();
tx.deposit(100); // Alice deposited 100.0

외부 멤버에 접근하기

내부 클래스는 비공개 필드와 메서드를 포함하여 외부 클래스의 모든 멤버에 접근할 수 있습니다.

class Encryption {
    private final String secretKey = "s3cr3t"; // private!

    class Encryptor {
        String encrypt(String data) {
            // Inner class accesses private outer field
            return data + "|" + secretKey.hashCode();
        }
    }

    class Decryptor {
        String decrypt(String encrypted) {
            int idx = encrypted.lastIndexOf('|');
            return encrypted.substring(0, idx);
        }
    }
}

현재 객체 참조로 이름 가리기

내부 클래스에 외부 클래스와 같은 이름의 필드가 있을 때는 OuterClass.this.field를 사용하여 외부 클래스의 필드를 참조합니다.

class Outer {
    int value = 10;

    class Inner {
        int value = 20; // shadows outer.value

        void printBoth() {
            System.out.println("Inner value: " + value);         // 20
            System.out.println("Outer value: " + Outer.this.value); // 10
        }
    }
}

new Outer().new Inner().printBoth();
// Inner value: 20
// Outer value: 10

내부 클래스를 사용한 반복자 패턴

대표적인 사용 사례는 외부 컬렉션의 비공개 데이터에 접근하는 반복자를 내부 클래스로 구현하는 것입니다.

class NumberRange implements Iterable<Integer> {
    private final int start;
    private final int end;

    NumberRange(int start, int end) {
        this.start = start; this.end = end;
    }

    @Override
    public java.util.Iterator<Integer> iterator() {
        return new RangeIterator(); // inner class instance
    }

    private class RangeIterator implements java.util.Iterator<Integer> {
        private int current = start; // accesses outer start

        public boolean hasNext() { return current <= end; }
        public Integer next()    { return current++; }
    }
}

for (int n : new NumberRange(1, 5)) System.out.print(n + " ");
// 1 2 3 4 5

내부 클래스로 구현하는 이벤트 리스너

GUI 및 이벤트 기반 프로그래밍에서는 외부 UI 구성 요소의 상태에 접근할 수 있도록 이벤트 리스너를 내부 클래스로 구현하는 경우가 많습니다.

// Swing-style example (conceptual)
class LoginPanel {
    private String username = "";

    class LoginButtonListener {
        void onButtonClicked() {
            if (username.isEmpty()) {
                System.out.println("Please enter username"); // accesses outer field
            } else {
                System.out.println("Logging in as: " + username);
            }
        }
    }

    void setUsername(String name) { this.username = name; }
}
LoginPanel panel = new LoginPanel();
panel.setUsername("alice");
panel.new LoginButtonListener().onButtonClicked();
// Logging in as: alice

컬렉션에서의 내부 클래스

일부 컬렉션 구현은 외부 컬렉션을 기반으로 동작하는 커서나 뷰를 보관하기 위해 내부 클래스를 사용합니다.

class SimpleStack<T> {
    private Object[] elements;
    private int size = 0;

    SimpleStack(int capacity) {
        elements = new Object[capacity];
    }

    void push(T item) {
        if (size >= elements.length) throw new StackOverflowError();
        elements[size++] = item;
    }

    T pop() {
        if (size == 0) throw new java.util.EmptyStackException();
        T item = (T) elements[--size];
        elements[size] = null;
        return item;
    }

    int size() { return size; }
}

메모리 누수 주의

내부 클래스 인스턴스는 외부 인스턴스에 대한 참조를 보유합니다. 내부 클래스가 외부 클래스의 범위를 벗어나 장수명 컬렉션에 저장되는 등의 상황에서는 외부 인스턴스를 가비지 컬렉션할 수 없습니다.

// Memory leak: anonymous inner class (implicit outer ref) stored statically
class LeakExample {
    byte[] data = new byte[1024 * 1024]; // 1MB

    // BAD: this Runnable holds a reference to LeakExample.this
    Runnable leak = new Runnable() {
        public void run() { System.out.println(data.length); }
    };
}
// If 'leak' is stored somewhere, LeakExample (and its 1MB) is never GC'd

// FIX: use a static nested class or a lambda that captures only needed values
static Runnable noLeak(byte[] data) {
    return () -> System.out.println(data.length); // captures array, not outer
}

내부 클래스와 람다 비교

최신 Java에서는 단일 메서드 인터페이스를 구현할 때 익명 내부 클래스를 람다로 대체하는 경우가 많습니다. 람다는 더 간결하지만 자체적인 현재 객체 참조를 갖지 않습니다.

// Old way: anonymous inner class
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Running (inner class) — this is the Runnable");
    }
};

// Modern: lambda
Runnable r2 = () -> System.out.println("Running (lambda)");

r1.run();
r2.run();

// Lambda this = enclosing class (not the Runnable)
// Inner class this = the Runnable instance itself

내부 클래스를 사용할 때

다음과 같은 경우 비정적 내부 클래스를 사용하세요:

  • 외부 인스턴스의 비공개 상태에 접근해야 하는 경우
  • 각 내부 인스턴스가 특정 외부 인스턴스와 논리적으로 연결되는 경우
  • 순회 로직을 캡슐화하는 반복자 패턴을 구현하는 경우

실습: Paginator

내부 클래스가 페이지 상태를 보유하고 외부 데이터 목록에 접근하는 실용적인 페이지 매김기입니다.

import java.util.*;

class Paginator<T> {
    private final List<T> items;
    private final int pageSize;

    Paginator(List<T> items, int pageSize) {
        this.items = items; this.pageSize = pageSize;
    }

    class Page {
        private int pageIndex = 0;

        List<T> current() {
            int start = pageIndex * pageSize;
            int end = Math.min(start + pageSize, items.size()); // outer
            return start < items.size() ? items.subList(start, end) : List.of();
        }
        boolean hasNext() { return (pageIndex + 1) * pageSize < items.size(); }
        void next() { if (hasNext()) pageIndex++; }
    }
}
Paginator<String> p = new Paginator<>(List.of("a","b","c","d","e"), 2);
Paginator.Page page = p.new Page();
System.out.println(page.current()); // [a, b]
page.next();
System.out.println(page.current()); // [c, d]

빠른 확인

정적 중첩 클래스에는 없고 내부(비정적) 클래스가 보유하는 것은 무엇입니까?

복습: 내부 클래스와 외부 클래스 접근

핵심 내용:

  • 비정적 내부 클래스는 자신을 둘러싼 외부 인스턴스에 대한 암시적 참조를 보유합니다
  • 내부 인스턴스는 외부 인스턴스를 통해서만 생성합니다: outer.new Inner()
  • 내부 클래스는 private 필드를 포함한 외부 클래스의 모든 멤버에 접근할 수 있습니다
  • 이름이 가려진 필드를 구분하려면 Outer.this.field를 사용합니다
  • 대표적인 사용 사례: Iterator, 이벤트 listener, 내부 데이터 구조
  • 위험 요소: 내부 클래스 인스턴스가 외부 인스턴스의 가비지 수집을 방해합니다

자주 묻는 질문

“내부 클래스와 외부 클래스 접근” 강의는 무료인가요?

네 — “내부 클래스와 외부 클래스 접근” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“내부 클래스와 외부 클래스 접근”에서 뭘 배우나요?

외부 클래스 인스턴스에 대한 참조를 보유하는 비정적 내부 클래스를 사용합니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“내부 클래스와 외부 클래스 접근” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 정적 중첩 클래스
  2. 내부 클래스와 외부 클래스 접근
  3. 지역 클래스와 익명 클래스
  4. 적절한 중첩 전략 선택
← Java Academy(으)로 돌아가기