0Pricing
Java Academy · レッスン

内部クラスと外部クラスへのアクセス

外部クラスのインスタンスへの参照を保持する、非 static の内部クラスを使用します。

「内部クラスと外部クラスへのアクセス」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

内部クラス

非staticの内部クラスは、外側のクラスのインスタンスへの暗黙的な参照を保持します。privateなメンバーを含め、外側のクラスのすべてのメンバーにアクセスできます。

内部クラスの宣言と使用

内部クラスのインスタンスは、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

外側のメンバーへのアクセス

内部クラスは、privateなフィールドやメソッドも含め、外側のクラスのすべてのメンバーにアクセスできます。

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);
        }
    }
}

thisによるシャドーイング

内部クラスに外側のクラスと同じ名前のフィールドがある場合は、外側のフィールドを参照するために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

内部クラスによるIteratorパターン

典型的な使用例は、外側のコレクションのprivateデータにアクセスする内部クラスとしてイテレータを実装することです。

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では、単一メソッドインターフェースに対して、匿名内部クラスの代わりにラムダ式を使うことがよくあります。ラムダ式はより簡潔ですが、独自のthisは持ちません。

// 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

内部クラスを使用する場合

次のような場合は、非staticの内部クラスを使用します:

  • 外側のインスタンスのprivateな状態にアクセスする必要がある場合
  • 各内部クラスのインスタンスが、特定の外側のインスタンスと論理的に結び付いている場合
  • 走査ロジックをカプセル化するIteratorパターンを使用する場合

実践: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]

クイックチェック

staticなネストクラスにはなく、内部クラス(非static)が保持するものは何ですか。

まとめ:内部クラスと外側へのアクセス

重要なポイント:

  • 非staticの内部クラスは、包含する外側のインスタンスへの暗黙的な参照を保持します
  • 内部クラスのインスタンスは、外側のインスタンスを通じてのみ作成します:outer.new Inner()
  • 内部クラスは、privateフィールドを含む外側のすべてのメンバーにアクセスできます
  • フィールド名の衝突を解消するには、Outer.this.fieldを使用します
  • 適した用途:Iterator、イベントリスナー、内部データ構造
  • 注意点:内部クラスのインスタンスがあると、外側のインスタンスはガベージコレクションの対象になりません

よくある質問

「内部クラスと外部クラスへのアクセス」レッスンは無料ですか?

はい。「内部クラスと外部クラスへのアクセス」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「内部クラスと外部クラスへのアクセス」で何を学びますか?

外部クラスのインスタンスへの参照を保持する、非 static の内部クラスを使用します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「内部クラスと外部クラスへのアクセス」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. static ネストクラス
  2. 内部クラスと外部クラスへのアクセス
  3. ローカルクラスと匿名クラス
  4. 適切なネスト方法の選択
← Java Academyに戻る