내부 클래스와 외부 클래스 접근
외부 클래스 인스턴스에 대한 참조를 보유하는 비정적 내부 클래스를 사용합니다.
내부 클래스와 외부 클래스 접근은(는) 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 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 정적 중첩 클래스
- 내부 클래스와 외부 클래스 접근
- 지역 클래스와 익명 클래스
- 적절한 중첩 전략 선택