Classi interne

D: Allora a cosa servono comunque le classi interne?

R: Le classi interne si annidano all'interno di altre classi. Una classe normale è un membro diretto di un pacchetto, una classe di primo livello. Le classi interne, che sono diventate disponibili con Java 1.1, sono disponibili in quattro versioni:

  • Classi membri statici
  • Classi dei membri
  • Classi locali
  • Classi anonime

Diamo una rapida occhiata a ciascuno di essi.

In breve, una classe membro statica è un membro statico di una classe. Come qualsiasi altro metodo statico, una classe membro statica ha accesso a tutti i metodi statici della classe padre o di primo livello.

Come una classe membro statica, anche una classe membro è definita come membro di una classe. A differenza della varietà statica, la classe membro è specifica dell'istanza e ha accesso a tutti i metodi e membri, anche al thisriferimento del genitore .

Le classi locali sono dichiarate all'interno di un blocco di codice e sono visibili solo all'interno di quel blocco, proprio come qualsiasi altra variabile di metodo.

Infine, una classe anonima è una classe locale che non ha nome.

Per rispondere alla tua domanda specifica, mi concentrerò sui membri e sulle classi interne anonime poiché quelle sono quelle che probabilmente incontrerai e utilizzerai. Per me, i vantaggi delle classi interne possono essere suddivisi in tre categorie: un vantaggio orientato agli oggetti, un vantaggio organizzativo e un vantaggio di richiamata.

Il vantaggio orientato agli oggetti

A mio modesto parere, la caratteristica più importante della classe interiore è che ti consente di trasformare le cose in oggetti che normalmente non trasformeresti in oggetti. Ciò consente al codice di essere ancora più orientato agli oggetti di quanto sarebbe senza classi interne.

Diamo un'occhiata alla classe dei membri. Poiché la sua istanza è un membro della sua istanza padre, ha accesso a ogni membro e metodo nell'istanza padre. A prima vista, potrebbe non sembrare molto; abbiamo già quel tipo di accesso dall'interno di un metodo nella classe genitore. Tuttavia, la classe membro ci consente di estrarre la logica dal genitore e di oggettivarla. Ad esempio, una classe albero può avere un metodo e molti metodi di supporto che eseguono una ricerca o una passeggiata dell'albero. Da un punto di vista orientato agli oggetti, l'albero è un albero, non un algoritmo di ricerca. Tuttavia, è necessaria una conoscenza approfondita delle strutture di dati dell'albero per eseguire una ricerca.

Una classe interna ci consente di rimuovere quella logica e inserirla nella sua classe. Quindi, da un punto di vista orientato agli oggetti, abbiamo tolto la funzionalità da dove non appartiene e l'abbiamo inserita nella sua classe. Attraverso l'uso di una classe interna, abbiamo disaccoppiato con successo l'algoritmo di ricerca dall'albero. Ora, per cambiare l'algoritmo di ricerca, possiamo semplicemente scambiarlo in una nuova classe. Potrei continuare, ma questo apre il nostro codice a molti dei vantaggi forniti dalle tecniche orientate agli oggetti.

Il vantaggio organizzativo

Il design orientato agli oggetti non è cosa di tutti, ma fortunatamente le classi interne forniscono di più. Da un punto di vista organizzativo, le classi interne ci consentono di organizzare ulteriormente la nostra struttura dei pacchetti attraverso l'uso di spazi dei nomi. Invece di scaricare tutto in un pacchetto semplice, le classi possono essere ulteriormente annidate all'interno delle classi. In modo esplicito, senza classi interne, eravamo limitati alla seguente struttura gerarchica:

pacchetto1 classe 1 classe 2 ... classe n ... pacchetto n 

Con le classi interne possiamo fare quanto segue:

pacchetto 1 classe 1 classe 2 classe 1 classe 2 ... classe n 

Se usate con attenzione, le classi interne possono fornire una gerarchia strutturale che si adatta più naturalmente alle vostre classi.

Il vantaggio di richiamata

Le classi dei membri interni e le classi anonime forniscono entrambe un metodo conveniente per definire i callback. L'esempio più ovvio si riferisce al codice della GUI. Tuttavia, l'applicazione della richiamata può estendersi a molti domini.

La maggior parte delle GUI Java ha un qualche tipo di componente che istiga una actionPerformed()chiamata al metodo. Sfortunatamente, la maggior parte degli sviluppatori ha semplicemente implementato la finestra principale ActionListener. Di conseguenza, tutti i componenti condividono lo stesso actionPerformed()metodo. Per capire quale componente ha eseguito l'azione, normalmente c'è un gigantesco e brutto interruttore nel actionPerformed()metodo.

Ecco un esempio di implementazione monolitica:

la classe pubblica SomeGUI estende JFrame implementa ActionListener {protected JButton button1; protetto JButton button2; ... protetto JButton buttonN; public void actionPerformed (ActionEvent e) {if (e.getSource () == button1) {// do something} else if (e.getSource () == button2) {... you get the picture

Ogni volta che vedi interruttori o blocchi if/ if elseblocchi grandi , i campanelli d'allarme dovrebbero iniziare a suonare nella tua mente. In generale, tali costrutti sono una cattiva progettazione orientata agli oggetti poiché una modifica in una sezione del codice può richiedere una modifica corrispondente nell'istruzione switch. Le classi dei membri interni e le classi anonime ci consentono di allontanarci dal actionPerformed()metodo cambiato .

Invece, possiamo definire una classe interna che implementa ActionListenerper ogni componente a cui vogliamo ascoltare. Ciò può comportare molte classi interne. Tuttavia, possiamo evitare istruzioni di commutazione di grandi dimensioni e avere il vantaggio aggiuntivo di incapsulare la nostra logica di azione. Inoltre, questo approccio può migliorare le prestazioni. In uno switch in cui sono presenti n confronti, possiamo aspettarci n / 2 confronti nel caso medio. Le classi interne ci consentono di impostare una corrispondenza 1: 1 tra l'esecutore dell'azione e l'ascoltatore dell'azione. In una GUI di grandi dimensioni, tali ottimizzazioni possono avere un impatto sostanziale sulle prestazioni. Un approccio anonimo può essere simile a questo:

la classe pubblica SomeGUI estende JFrame {... dichiarazioni dei membri del pulsante ... protected void buildGUI () {button1 = new JButton (); button2 = nuovo JButton (); ... button1.addActionListener (new java.awt.event.ActionListener () {public void actionPerformed (java.awt.event.ActionEvent e) {// do something}}); .. ripetere per ogni pulsante

Utilizzando le classi membri interne, lo stesso programma sarebbe simile a questo:

la classe pubblica SomeGUI estende JFrame {... dichiarazioni del membro del pulsante // la classe delle definizioni della classe interna Button1Handler implementa ActionListener {public void actionPerformed (ActionEvent e) {// fai qualcosa}} ... definisce una classe membro interna per ogni pulsante protetto void buildGUI () {// inizializza i pulsanti button1 = new JButton (); button2 = nuovo JButton (); ... // registra un'istanza listener di azioni di classe interna // per ogni pulsante button1.addActionListener (new Button1Handler ()); .. ripetere per ogni pulsante

Poiché le classi interne hanno accesso a tutto ciò che è nel genitore, possiamo spostare qualsiasi logica che sarebbe apparsa in actionPerformed()un'implementazione monolitica in una classe interna.

Preferisco usare le classi membri come callback. Tuttavia, è una questione di preferenze personali. Sento solo che troppe classi anonime ingombrano il codice. Ritengo inoltre che le classi anonime possano diventare ingombranti se sono più grandi di una o due righe.

Svantaggi?

Come per qualsiasi altra cosa, devi prendere il buono con il cattivo. Le classi interne hanno i loro svantaggi. Da un punto di vista della manutenzione, gli sviluppatori Java inesperti potrebbero trovare difficile comprendere la classe interna. L'uso di classi interne aumenterà anche il numero totale di classi nel codice. Inoltre, dal punto di vista dello sviluppo, la maggior parte degli strumenti Java è un po 'a corto di supporto delle classi interne. Ad esempio, utilizzo VisualAge di IBM per Java per la mia programmazione quotidiana. Mentre le classi interne verranno compilate all'interno di VisualAge, non esiste un browser o modello della classe interna. Invece, devi semplicemente digitare la classe interna direttamente nella definizione della classe. Questo purtroppo rende difficile navigare nella classe interna. È anche difficile digitare poiché perdi molti di VisualAge 's il completamento del codice aiuta quando si digita nella definizione della classe o si utilizza una classe interna.

Tony Sintes è un consulente senior presso ObjectWave, specializzato in telecomunicazioni. Sintes, un programmatore Java 1.1 certificato da Sun e sviluppatore Java 2, lavora con Java dal 1997.

Ulteriori informazioni su questo argomento

  • "Inner Classes Specification", di Sun, fornisce uno sguardo approfondito alle classi interne

    //java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html

Questa storia, "Inner classes" è stata originariamente pubblicata da JavaWorld.