CodeGym /Corsi /JAVA 25 SELF /Classi anonime

Classi anonime

JAVA 25 SELF
Livello 16 , Lezione 2
Disponibile

1. Introduzione alle classi anonime

Immagina di avere la classe Animal, che descrive come si comporta un animale. Questa classe ha il metodo say(), che stampa "L'animale emette un suono". Devi creare un oggetto che si comporti come un cane e stampi "Bau!", ma non vuoi creare per questo un file separato Dog.java.

Ecco dove entrano in gioco le classi anonime. Sono classi senza nome, dichiarate e create direttamente nel punto in cui vengono utilizzate. 🚀 Consentono di estendere “al volo” una classe senza creare un file separato. Invece di moltiplicare tanti piccoli file di classi usate in un solo punto, puoi scrivere tutta la logica proprio là dove serve.

Com’è la sintassi?

La sintassi delle classi anonime può sembrare insolita, ma in realtà è piuttosto semplice:

TipPeremennoi imya = new TipDlyaNasledovaniya() {
    // Qui scriviamo il corpo della classe anonima
    // Sovrascriviamo i metodi della superclasse
};

Vediamo nel dettaglio questa sintassi:

  • TipPeremennoi imya: È una normale dichiarazione di variabile in cui salveremo il nostro nuovo oggetto.
  • new TipDlyaNasledovaniya(): Qui sembra che stiamo creando un nuovo oggetto. Ma invece del nome di una nuova classe usiamo il nome della classe da cui vogliamo ereditare. Nota che dopo le parentesi () non c’è il punto e virgola !
  • { ... }: Qui apriamo le parentesi graffe e scriviamo tutta la logica della nostra classe anonima. Possiamo sovrascrivere i metodi della superclasse o aggiungere la nostra logica.

Esempio di estensione di una classe normale:

Torniamo al nostro esempio con l’animale.

class Animal {
    void say() {
        System.out.println("L'animale emette un suono");
    }
}

// Creiamo una classe anonima estendendo Animal
Animal dog = new Animal() {
    // Sovrascriviamo il metodo say()
    @Override
    void say() {
        System.out.println("Bau-bau! 🐶");
    }
};

Animal cat = new Animal() {
    @Override
    void say() {
        System.out.println("Miao-miao! 🐱");
    }
};

dog.say(); // Stamperà: Bau-bau! 🐶
cat.say(); // Stamperà: Miao-miao! 🐱

In questo esempio abbiamo creato due oggetti, dog e cat, che sono di fatto classi anonime che estendono Animal. E non abbiamo creato alcun file separato.

Confronto degli approcci

// Metodo tradizionale (creiamo una classe separata)
class Dog extends Animal {
@Override
void say() { System.out.println("Bau!"); }
}
Animal dog = new Dog();

// VS

// Classe anonima (tutto in un unico posto)
Animal dog = new Animal() {
@Override
void say() { System.out.println("Bau!"); }
};

Il risultato è lo stesso, ma la classe anonima è più concisa e non appesantisce il progetto!

2. Nome della classe anonima dopo la compilazione

Le classi anonime non hanno un nome nel codice sorgente, ma il compilatore Java deve naturalmente chiamarle in qualche modo per creare il file .class. Lo fa seguendo una regola ben definita:

  • Il nome del file della classe anonima è composto dal nome della classe esterna in cui è stata dichiarata.
  • Dopo il nome della classe esterna si aggiunge il simbolo del dollaro $.
  • Segue il numero progressivo della classe anonima in quel file, a partire da 1.

Di conseguenza, se il nostro esempio con gli animali si trova nel file Main.java, dopo la compilazione verranno creati tre file:

  • Main.class
  • Main$1.class (la nostra classe anonima per il cane)
  • Main$2.class (la nostra classe anonima per il gatto)

Se la classe anonima è dichiarata dentro un metodo che a sua volta si trova in una classe interna, il nome sarà più complesso, ad esempio, OuterClass$InnerClass$1.class.

È una convenzione interna del compilatore, utile da conoscere, ma che raramente ha un ruolo importante nello sviluppo quotidiano. L’importante è ricordare che una classe anonima è comunque una classe a tutti gli effetti, anche se senza nome nel codice sorgente.

3. Caratteristiche e limitazioni importanti

Le classi anonime sono uno strumento potente, ma hanno le loro regole.

Accesso alle variabili. Una classe anonima può usare variabili del metodo che la circonda. Tuttavia tali variabili devono essere final o effectively final (cioè il loro valore non cambia dopo l’inizializzazione).

public void doSomething() {
    String greeting = "Ciao!"; // Questa variabile è effectively final

    class OuterClass {
        void greet() {
            // Creiamo una classe anonima all'interno del metodo
            new Object() {
                void sayHello() {
                    System.out.println(greeting); // È consentito
                    // greeting = "Ciao!"; // Questo invece causerà un errore!
                }
            }.sayHello();
        }
    }

    new OuterClass().greet();
}

Perché? Perché una classe anonima può “vivere” più a lungo del metodo stesso e, se potesse modificare la variabile, ciò porterebbe a problemi.

Nessun costruttore. Poiché una classe anonima non ha un nome, non può avere un costruttore. Ma puoi usare un blocco di inizializzazione per eseguire codice alla creazione dell’oggetto:

Animal dog = new Animal() {
    // Blocco di inizializzazione
    {
        System.out.println("Inizializzazione della classe anonima 🐶");
    }
    @Override
    void say() {
        System.out.println("Bau-bau!");
    }
};

Limitazioni. Le classi anonime non possono dichiarare campi o metodi statici (tranne le costanti). Sono sempre create come parte di un altro oggetto, quindi non possono essere static, public, protected o private.

4. Dettagli utili

Quando usare le classi anonime

  • Hai bisogno di estendere una classe (o implementare un’interfaccia) solo una volta.
  • L’implementazione è piccola — 1–2 metodi, non più di qualche decina di righe.
  • La classe serve solo in un punto e non ha senso darle un nome.
  • Vuoi evitare di “ingolfare” il package con molte piccole classi usa-e-getta.

Scenari tipici:

  • Gestori di eventi (GUI, Swing, Android, ecc.).
  • Passaggio di callback ai metodi.
  • Implementazioni rapide di Comparator per ordinare le collezioni.
  • Oggetti temporaneamente modificati (ad esempio per i test).

5. Interazione con la classe esterna

Se una classe anonima è dichiarata all’interno di un metodo o di un blocco non statico della classe esterna, può accedere ai campi e ai metodi di tale classe (anche a quelli privati!).

public class Outer {
    private String secret = "Testo segreto";

    // Classe base
    class Printer {
        public void print() {
            System.out.println("Output normale");
        }
    }

    public void revealSecret() {
        Printer p = new Printer() {
            @Override
            public void print() {
                System.out.println("Accesso al privato: " + secret);
            }
        };
        p.print();
    }

    public static void main(String[] args) {
        new Outer().revealSecret();
    }
}

6. Errori tipici nell’uso delle classi anonime

Errore n. 1: tentativo di modificare una variabile del metodo circostante.
Se dichiari una variabile fuori dalla classe anonima e provi a modificarla dopo averla usata all’interno della classe anonima — il compilatore segnalerà un errore. La variabile deve essere final o effectively final (non cambia dopo l’inizializzazione).

Errore n. 2: classe anonima troppo grande.
Se una classe anonima cresce fino a decine di righe e contiene diversi metodi — è un segnale che conviene estrarla in una normale classe con nome. Altrimenti il codice diventa poco leggibile.

Errore n. 3: tentativo di usare metodi o campi statici.
In una classe anonima non si possono dichiarare metodi o campi statici (tranne le costanti). Se è davvero necessario — è il caso di creare una normale classe annidata.

Errore n. 4: dimenticare l’ambito di visibilità.
Una classe anonima è visibile solo nel punto in cui è dichiarata e non ha un nome. Se serve un riutilizzo multiplo — dichiara una classe normale.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION