Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 26 di 134
  • livello avanzato
Indice lezioni

Classi anonime

Utilizzare una classe "locale" senza un nome assegnato, una istanza creata un'unica volta attraverso una singola espressione con l'operatore new.
Utilizzare una classe "locale" senza un nome assegnato, una istanza creata un'unica volta attraverso una singola espressione con l'operatore new.
Link copiato negli appunti

Una classe anonima è una classe "locale" senza un nome assegnato, si tratta di una classe definita e instanziata un'unica volta attraverso una singola espressione caratterizzata da una versione estesa della sintassi dell'operatore new.

Quindi, mentre le classi inner sono definite in tutto e per tutto in maniera identica a come si definiscono le comuni classi, con la classica dichiarazione che fa uso della keyword class), le classi anonime sono definite ed istanziate in un'unica espressione.

Sintassi delle classi anonime

La sintassi per definire una classe anonima è idetica a quella utilizzata per la chiamata di un costruttore, eccetto per il fatto che la chiamata al costuttore è seguita da un blocco di codice che estende la classe stessa (o implementa metodi nel caso si crei una istanza anonima di una intefaccia).

A seconda che la classe anonima estenda una classe o implementi un'interfaccia si possono avere due varianti:

new ClassName ( [ argument-list ] ) { class-body }

oppure:

new InterfaceName () { class-body }

in cui la differenza consiste nel fatto che quando si istanzia anonimamente una classe si deve usare uno dei costruttori disponibili, al quale vanno dunque passati gli opportuni argomenti, mentre per istanziare una classe anonima basata su una interfaccia (che quindi la implementerà) è possibile fare uso solamente del costruttore 'default' senza argomenti.

Le classi anonime consentono di rendere il codice molto più conciso, dando la possibilità di dichiarare ed instanziare una classe al tempo stesso e devono essere considerate in tutto e per tutto identiche alle classi (inner), eccetto per il fatto che non hanno un nome.

Possiamo utilizzare le classi anonime quando abbiamo la necessità di utilizzare una classe localmente ed una volta sola. Consideriamo il seguente esempio, dove è data una interfaccia:

public interface TitledName {
	public String femaleTitle(String name);
	public String maleTitle(String name);
}

il modo "classico" di instanziare un oggetto sarebbe:

BaseTitle frenchTitle = new FrenchTitle();

che dovrebbe essere corredato dalla definizione di una opportuna classe che implementi l'interfaccia

public class FrenchTitle implements TitledName {

	@Override
	public String femaleTitle(String name) {
		return "Mademoiselle "+name;
	}
	@Override
	public String maleTitle(String name) {
		return "Monsieur "+name;
	}
}

mentre una Instanza di un oggetto anonimo può essere ottenuta semplicemente ed in un sol colpo come:

BaseTitle englishTitle= new TitledName() {

	@Override
	public String femaleTitle(String name) {
		return "Ms "+name;
	}
	@Override
	public String maleTitle(String name) {
		return "Mister "+name;
	}
};

Nell'esempio possiamo notare che le classi anonime hanno caratteristiche precise:

  • dichiarazione attraverso l'operatore new
  • interfaccia da implementare o classe da estendere TitledName
  • parentesi con gli argomenti per il costruttore
  • il corpo della classe; in particolare, all'interno del corpo di definizione della classe anonima sono consentiti metodi, ma non istruzioni singole; naturalmente quando si istanzia anonimamente una interfaccia tutti i metodi devono essere implementati e allo stesso modo: tutti i metodi virtuali di una classe astratta andrebbero implementati.

Poiché la definizione di una classe anonima è un'espressione, deve essere parte di un'istruzione; nell'esempio sopra si tratta dell'istruzione di dichiarazione ed inizializzazione dell'oggetto englishTitle ;

Per le classi anonime valgono sostanzialmente le medesime regole delle classi locali per quanto riguarda l'accesso alle variabili locali:

  • una classe anonima ha accesso a tutti i membri della classe che la racchiude (anche a quelli privati)
  • Una classe anonima può essede definita anche dentro al corpo di un metodo, in tal caso sono visibile per la classe anonima solo le variabili final (ed effectively final da java 8)

In una classe anonima si possono dichiarare campi, metodi (anche se non implementano nessun metodo astratto della classe che si estende o dell'interfaccia che si implementa), classi locali ma non si possono definire costruttori, e per quanto riguarda i membri si applicano le medesime regole delle classi inner e non si possono avere membri statici tranne che le costanti.

Le classi anonime sono una caratteristica del linguaggio Java utilizzata in numerosi contesti ma probabilmente il loro uso più comune è quando ci si trovano a realizzare interfacce grafiche dove il predominante approccio "a eventi

Per esempio per realizzare una intefaccia che utilizzi un bottone con JavaFX

public void start(Stage primaryStage) {
	Button btn = new Button();
	btn.setText("Click");
	btn.setOnAction(new EventHandler<ActionEvent>() {
			@Override
			public void handle(ActionEvent event) {
				// instructions
			}
		});
}

dove l'effetto del "click" sul bottone è controllato da un oggetto (chiamato in questo caso EventHandler Listener

In dettaglio nell'esempio si specifica l'azione che verrà effettuata al click sul bottone "Click" usando il metodo setOnAction EventHandler<ActionEvent>

Vale la pena di osservare che a causa del fatto che l'interfaccia EventHandler lambda expression Lambda espression

Per tutte le classi nested vale la regola che la eventuale dichiarazione di una variabile, membro o paramentro che abbia il medesimo nome di una variabile, metodo o parametro della classe (o metodo) ospitante "shadows" (copre) la dichiarazione più esterna. In un esempio:

public class Ospite {
	int variabile = 5;
	ClasseInner classeInner = new NomeInterfaccia() {
		int variabile = 6;
		// ...
		@Override
		public metodoOverridden(...) {
			System.out.println(variabile);  // STAMPA 6
		}
		@Override
		public altroMetodoOverridden(...,int variabile,...) {
			System.out.println(variabile); // STAMPA il valore passato come parametro
			System.out.println(this.variabile); // STAMPA 6, field di questa classe
			System.out.println(Ospite.this.variabile); // STAMPA 5, field della classe Ospite
		}
	}
}

Ti consigliamo anche