0Pricing
Java Academy · Lezione

Classi interne e accesso alla classe esterna

Utilizzi classi interne non statiche che mantengono un riferimento all'istanza della classe esterna.

Classi interne e accesso alla classe esterna è una lezione Java Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Java Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Java Academy include 4 lezioni in totale.

Classi interne

Una classe interna non statica contiene un riferimento implicito all'istanza della classe esterna che la contiene. Può accedere a tutti i membri della classe esterna, inclusi quelli privati.

Dichiarazione e utilizzo di una classe interna

Crei un'istanza della classe interna solo tramite un'istanza della classe esterna, utilizzando 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

Accesso ai membri della classe esterna

Le classi interne possono accedere a tutti i membri della classe esterna, inclusi i campi e i metodi privati.

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

Shadowing con this

Quando una classe interna ha un campo con lo stesso nome della classe esterna, utilizzi OuterClass.this.field per fare riferimento a quello esterno.

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

Pattern Iterator con una classe interna

Un caso d'uso classico consiste nell'implementare un iteratore come classe interna che accede ai dati privati della raccolta che la contiene.

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

Listener di eventi come classi interne

La programmazione con GUI e a eventi utilizza comunemente classi interne per implementare listener di eventi con accesso allo stato del componente dell'interfaccia che le contiene.

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

Classe interna nelle raccolte

Alcune implementazioni delle raccolte utilizzano classi interne per contenere cursori o viste supportate dalla raccolta che le contiene.

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

Avviso sulle perdite di memoria

Le istanze delle classi interne mantengono un riferimento all'istanza esterna. Se la classe interna fuoriesce dalla classe esterna (ad esempio, perché viene memorizzata in una raccolta di lunga durata), l'istanza esterna non può essere raccolta dal garbage collector.

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

Confronto tra classe interna e lambda

Le lambda moderne di Java spesso sostituiscono le classi interne anonime per le interfacce a metodo singolo. Le lambda sono più concise, ma NON hanno un proprio 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

Quando utilizzare le classi interne

Utilizzi classi interne non statiche quando:

  • La classe deve accedere allo stato privato dell'istanza che la contiene
  • Ogni istanza interna è logicamente legata a una specifica istanza esterna
  • Utilizza il pattern Iterator per incapsulare la logica di attraversamento

Esercitazione pratica: Paginator

Un paginatore pratico in cui la classe interna conserva lo stato della pagina e accede all'elenco di dati della classe esterna.

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]

Verifica rapida

Che cosa contiene una classe interna (non statica) che una classe annidata statica non contiene?

Riepilogo: classi interne e accesso alla classe esterna

Punti chiave:

  • Le classi interne non statiche conservano un riferimento implicito all'istanza esterna che le contiene
  • Creare istanze interne solo tramite un'istanza esterna: outer.new Inner()
  • Le classi interne possono accedere a tutti i membri della classe esterna, inclusi i campi privati
  • Utilizzare Outer.this.field per distinguere i campi con lo stesso nome
  • Casi d'uso ideali: Iterator, listener di eventi, strutture dati interne
  • Rischio: le istanze delle classi interne impediscono che le istanze esterne vengano raccolte dal garbage collector

Domande Frequenti

La lezione «Classi interne e accesso alla classe esterna» è gratuita?

Sì — il testo completo di «Classi interne e accesso alla classe esterna» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Java Academy, passa a CoddyKit PRO. Il corso Java Academy include 4 lezioni in totale.

Cosa imparerò in «Classi interne e accesso alla classe esterna»?

Utilizzi classi interne non statiche che mantengono un riferimento all'istanza della classe esterna. Eserciti Java Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Java Academy?

Non è richiesta alcuna esperienza precedente. Java Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Classi interne e accesso alla classe esterna»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Java Academy?

Sì. Ogni lezione Java Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Classi annidate statiche
  2. Classi interne e accesso alla classe esterna
  3. Classi locali e anonime
  4. Scegliere la strategia di annidamento più adatta
← Torna a Java Academy