Nested Classes

Static nested classes

public class WireFrameModel {

   . . . // other members of the WireFrameModel class
   
   static public class Line {
         // Represents a line from the point (x1,y1,z1)
         // to the point (x2,y2,z2) in 3-dimensional space.
      double x1, y1, z1;
      double x2, y2, z2;
   } // end class Line
   
   . . . // other members of the WireFrameModel class
   
} // end WireFrameModel

Il nome completo della nested class è WireFrameModel.Line. Questo nome può essere utilizzato ad esempio per dichiarare le variabili. All'interno di WireFramaModel un oggetto di tipo Line, sarebbe dichiarato con new Line(). All'esetno della classe contenitore con new WireFrameModel.Line().

Una static nested class, ha accesso completo ai membri statici della classe contenitore, anche se sono membri privati.

Inner class

Una nested class non static, è chiamata una inner class. Una Inner class, non è in pratica molto differente da una nested class, ma una non-static nested class è associata con un oggetto piuttosto che con la classe al cui interno è definita.

I membri non statici di una classe specificano cosa verrà contenuto negli oggetti che sono creati dalla classe. Lo stesso è vero per le inner class. Cioè ogni oggetto che appartiene alla classe contenitore ha la sua copia della nested class. Questa classe ha accesso a tutti i membri e metodi d'istanza dell'oggetto anche se sono dichiarati private. Le due copie dell'inner class in due oggetti differenti differiscono perché le variabili d'istanza e i metodi d'istanza a cui si riferiscono sono in oggetti differenti. In effetti, la regola per decidere se una nested class, devono essere static o non-static è semplice: Se la nested class ha bisogno di accedere ai membri o metodi d'istanza della classe contenitore, creaimo la nested class, non-static. Altrimenti facciamola static.

public class PokerGame {  // Represents a game of poker.
    
    class Player {  // Represents one of the players in this game.
       .
       .
       .
    } // end class Player
    
    private Deck deck;      // A deck of cards for playing the game.
    private int pot;        // The amount of money that has been bet.
    
    .
    .
    .

} // end class PokerGame

Se game è una variabile di tipo PokerGame, allora, concettualmente, game contiene la propria copia della classe Player. In un metodo d'instanza di un oggetto di tipo PokerGame, un nuovo oggetto di tipo Player, verrà creato con istruzione "new Player()", così come ogni altra classe. (Un oggetto invece Player, che volesse essere creato dall'esterno della classe PockerGame, utilizzo più raro, dovrebbe essere creato tramite l'istruzione "game.new Player()"). L'oggetto di tipo Player avrebbe accesso alle variabili d'istanza deck e pot dell'oggetto di tipo PokerGame. Ogni oggetto di tipo PokerGame ha il proprio deck, pot e Players. I Player di quel particolare poker game, partita di poker, utilizzano il mazzo (deck) e puntata (pot) per quella particolare partita; giocatori di un altro poker game, partita, avranno un altro mazzo, deck, e puntata, pot. Questo è l'effetto di avere la classe Player non-static. Questo è il modo naturale per i giocatori di comportarsi. Un oggetto di tipo Player rappresenta un giocatore in una particolare partita di poker. Se Player fosse stata una classe indipendente o una classe statica, d'altro lato, essa avrebbe rappresentato l'idea generale di giocatore di poker, indipendentemente dalla particolare partita di poker.

Anonymous Inner classes

In alcuni casi, vi potresete trovare a scrivere un' inner class e a utilizzare quella classe in usa sol punto del vostro codice. Ha senso allora creare una classe? Può darsi, ma potreste anche creare un anomymous inner class. Un' anomymous inner class, è creata con una con una variazione dell'operatore new, che ha la forma:

new  <superclass-or-interface> ( <parameter-list> ) {
                   <methods-and-variables>
              }

Questo costruttore definisce una nuova classe senza assegnargli un nome. Questa forma dell'operatore new è creare "un nuovo oggetto appartenente a una classe che è la stessa come <superclass-or-interface> ma con questi <methods-and-variables> aggiunti. Il risultato è ottenere un oggetto unico customizzato, giusto nel punto del programma in cui è necessario. Da notare che è possibile basare una classe anonima su un'interfaccia, piuttosto che una classe. In questo caso, una classe anonima, deve implementare un'interfaccia definendo tutti i metodi che sono dichiarati nell'interfaccia. Se un'interfaccia è utilizzata come base, la <parameter-list> deve essere vuota. Altrimenti, può contenere i parametri del costruttore della <superclass>.

Supponiamo di avere un'interfaccia Drawable così definita:

public interface Drawable {
    public void draw(GraphicsContext g);
}

Se vogliamo utilizzare un oggetto di tipo Drawable di colore rosso che sia un rettangolo, invece di creare una nuova classe che implementi Drawable e poi instanziala per creare l'oggetto, possimo invece usare una classe anonima per creare l'oggetto direttamente.

Drawable redSquare = new Drawable() {
       void draw(Graphics g) {
          g.setColor(Color.RED);
          g.fillRect(10,10,100,100);
       }
   };

La variabile redSquare riferisce a un oggetto che implementa Drawable e che disegna un rettangolo rosso quando il metodo draw() è invocato.

Una classe anonima è spesso utilizzata come parametri attuali. Per esempio considera questa funzione:

void drawTwice( Graphics g1, Graphics g2, Drawable figure ) {
    figure.draw(g1);
    figure.draw(g2);
}

Quando chiamiamo questo metodo, il terzo parametro può essere creato utilizzano una classe anonima. Per esempio:

drawTwice( firstG, secondG, new Drawable() {
          void draw(Graphics g) {
             g.drawOval(10,10,100,100);
          }
     } );

Local classes

Una classe può essere definita pure all'interno di un metodo. Queste classi cono chiamate local classes, classi locali. Una local class può essere solo utilizzata all'interno del metodo in cui è definita. Tuttavia un oggetto che è definito da una local class può essere utilizzato all'esterno del metodo. Può essere ritornato come valore di una subroutine, o può essere passato come parametro di un altro metodo. Questo è possibile, perché un oggetto appartenente a qualche classe B può essere assegnato a una variabile di tipo A, se B è sottoclasse di A o, se la classe B implementa l'interfaccia A. Ad esempio se un metodo accetta come parametro un oggetto di tipo Drawable (interfaccia definita negli esempi sopra), allora un oggetto che implementa Drawable può essere passato come parametro a quel metodo. E quell'oggetto può essere definito da una local class.

La definizione di una local class può utilizzare le variabili locali del metodo all'interno del quale è definita. Può utilizzare anche parametri di quel metodo. Tuttavia c'è una restrizione nell'utilizzo di queste variabili locali e parametri nella local class: Le variabili locali o i parametri devono essere dichiarati final o, se non esplicitamente dichiarati final, devono esser "effettivamente final". Un parametro è "effettivamente final" se il suo valore non è cambiato all'interno della subroutine (inclusa ogni local class che riferisce il parametro). Una variabile locale è "effettivamente final" se il suo valore non è mai cambiato dopo che viene inizializzata. Nota che non c'è una simile restrizione sulle variabili globali che sono usate nella definizione di una local class.

Last updated