La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

INNER CLASS Una Inner class è una classe la cui dichiarazione si trova all’ interno di un’ altra classe (La classe ospitante è detta top level): class.

Presentazioni simili


Presentazione sul tema: "INNER CLASS Una Inner class è una classe la cui dichiarazione si trova all’ interno di un’ altra classe (La classe ospitante è detta top level): class."— Transcript della presentazione:

1 INNER CLASS Una Inner class è una classe la cui dichiarazione si trova all’ interno di un’ altra classe (La classe ospitante è detta top level): class A { . class InnerA { // codice di InnerA } // altro codice di A Per creare un oggetto di tipo InnerA si usa la seguente sintassi: A a=new A(); A.InnerA innerOb = a.new InnerA(); Se InnerA fosse stata a sua volta contenitrice di un’ altra classe B occorreva scrivere A.InnerA.B b=innerOb.new B();

2 Inner class (2) Confronto fra inner class e classi top-level
Come anche le classi to level, le inner class non possono avere il nome uguale a quello di altre classi top level Possibilità di una inner class di essere privata, cioè invisibile all’ esterno della classe ospite(ma si può andare oltre, facendo si che essa sia visibile solo all’ interno di un singolo metodo; vedremo poi). Una classe top level non può mai essere privata. Se si cercasse comunque di accedere da una classe esterna E alla inner privata di A il compilatore replicherebbe: Top.java:15: Inner type InnerA in class A not accessible from class E. 3) La inner class puó accedere a tutti i metodi e i campi della classe ospitante (mentre la classe ospitante può vedere solo la parte pubblica della inner class) 4) Un oggetto di classe inner non può esistere se non esiste un oggetto della classe ospitante (se si fosse omessa l’ istruzione A a=new A(); il compilatore avrebbe protestato dicendo che non esiste un oggetto “a” di classe A per istanziarne uno della classe inner). 5) Come conseguenza del punto 4) una inner class non può avere campi statici Confronto con il C++: in C++ le classi inner non hanno la proprietà 3); sono quindi meno potenti che in java

3 il compilatore vedrà la inner class nel seguente modo: class InnerA {
Come fa un oggetto di classe InnerA a utilizzare campi e metodi dell’ oggetto contenitore? In realtà il compilatore inserisce “segretamente” un riferimento all’ oggetto contenitore Supponendo che tale riferimento si chiami outer(in realtà non si chiama così; vedremo poi) il compilatore vedrà la inner class nel seguente modo: class InnerA { private outer A; InnerA( A a) { outer=a; } // non scrivere cosí in un programma vero! . outher.campoDiA; outer.metodoDiA(); A InnerA outer campoDiA NB: Non si può utilizzare outer in un programma(non è sintassi java!): si tratta solo di una rappresentazione interna del compilatore. Per riferirsi esplicitamente all’ oggetto contenitore bisogna usare la sintassi nomeClasseContenitore.this

4 Perché usare le inner class?
Il riferimento esplicito all’ oggetto contenitore è necessario in caso si voglia accedere a un suo campo e questo campo e’ anche il nome di un campo di InnerA(cioé negli stessi casi in cui é obbligatorio usare this in una classe normale) : class A { int omonimo=5; class InnerA { int omonimo=2; public void stampaOmonimi() { System.out.println(omonimo); // stampa 2 System.out.println(A.this.omonimo); // stampa 5 } Se però ci si vuole riferire a un campo statico di A la notazione esplicita da usare è A.nomeCampoSatico(infatti i campi statici non appartengono a nessun oggetto, neanche all’ oggetto contenitore) Perché usare le inner class? Le inner class, sebbene prevedano una sintassi più complessa danno una certa semplicità strutturale. Si supponga ad esempio di voler realizzare uno stack di interi:

5 Esempio di uso delle inner class
Supponendo di voler realizzare tale stack dinamicamente , è necessario creare una classe “LinkedNode”: classs LinkedNode { int info; LinkedNode next; } Tali campi dovranno inoltre essere pubblici per poter permettere il collegamento con il puntatore al top dello stack. Tuttavia non è opportuno che LinkedNode sia visibile anche all’ esterno della classe stack, in quanto tale classe è l’ unica a dover usare LinkedNode. Soluzione: rendiamo LinkedNode inner class privata di class:

6 class Stack { // stack di interi realizzato con la inner class
LinkedNode first; private class LinkedNode { private int info; LinkedNode next; public int getInfo() { return info; } public void set(int x) { info=x; } } public boolean isEmpty() { return first==null; } public int top() { return first.getInfo(); } public void pop() { if(isEmpty()==false) first = first.next; } public void push(int x) { LinkedNode nuovo=new LinkedNode(); nuovo.set(x); nuovo.next=first; first=nuovo;

7 Inner class staiche Le inner class oltre a poter essere private possono anche essere di tipo static. Le inner class statiche non dipendono da istanze della classe contenitore e, per questo motivo, non hanno alcun riferimento implicito a oggetti di tale classe. Questo ha una conseguenza importante: tutti i campi non static della classe contenitore non sono accessibili alle i.c. statiche(quindi sono come le inner class del C++). (Top.java:11: Can't make a static reference to nonstatic variable a in class Top.) Tuttavia rispetto ad una classe top-level è possibile accedere a tutte le variabili statiche, anche quelle private. ATTENZIONE: che una inner class sia static non significa che essa non è instanziabile; significa solo(come detto sopra) che un oggetto di tale classe non dipende dall’ esistenza di istanze della classe base. Per creare un oggetto ob di una inner class statica StaticInn contenuta in una classe Top si usa la notazione Top.StaticInn ob=new Top.StaticInn(); Non è però scorretto scrivere Top.StaticInn ob=top.new StaticInn(); (top di tipo Top), ma la prima notazione è preferibile, in quanto rispecchia meglio la peculiarità delle i.c statiche di non dipendere da istanze. Nota finale: si può abbreviare la dichiarazione importando la classe contenitrice; esattamente come se si trattasse di un package; ad esmpio: import Top.StaticInn; /* ….. */ StaticInn ob=new StaticInn(); // senza Top.

8 Utilizzo di i.c statiche: esempio
L’ uso principale delle i.c. statiche è di evitare conflitti di nomi. Si supponga di realizzare una classe che trovi il minimo e il massimo di un array, con due metodi (statici) int getMin(int a[]) e int getMax(int a[]) Se venisse invocato prima getMin() e poi getMax() sullo stesso array, occorrerebbero due scansioni dell’ array, mentre salta subito all’ occhio che si sarebbe potuto far tutto in una sola passata restituendo contemporaneamente sia il minimo che il massimo. Ciò potrebbe essere fatto creando una classe Pair contenenti due campi min e max e sostituendo i due metodi con un metodo getMinMax() che restituisca un oggetto di tipo Pair: Class Pair { int min; int max; } Class Esempio { …. // altro codice… static Pair getMinMax(int a[]) { // trova min e max di a[] e mette il risultato // in un oggetto Pair */ }

9 Il problema è che Pair è un nome piuttosto comune, e in un grosso progetto c’ è
eventualità che qualche altro programmatore abbia usato una sua class Pair. Questa conflittualità di nomi può essere evitata rendendo la classe Pair(la nostra!) i.c di Esempio. In questa maniera tale classe sarà disponibile all’ esterno con la notazione più univoca Esempio.Pair : class Esempio { public static class Pair { int min; int max; Pair(int min, int max) { this.min=min; this.max=max; } } static Pair getMinMax() { . return Pair(min,max); // leggi note! Dall’ esterno si scriverà Esempio.Pair=Esempio.getMinMax(myArray) , senza possibilità di confusione con altre classi di nome Pair NB: se Pair non fosse static il ompilatore si lamenterebbe dicendo che non esiste alcun oggetto di tipo 'Eempio' per utilizzare o creare istanze della i.c Pair

10 Classi locali a metodi Può capitare che ad una inner class non siano interessati tutti i metodi della classe base, ma solo un metodo di quest’ ultima. Si può allora fare di piú: dichiarare la classe all’ interno del metodo stesso(classe locale). Una classe locale non solo non è visibile dall’ esterno, ma neanche dagli altri metodi della classe ospite. Per capire l’ utilizzo di simili classi(e quindi quello delle classi anonime) facciamo un esempio. Introduciamo una interfaccia “Propriety”: una proprietà è un qualcosa che ha un nome e un valore; quindi l’ interfaccia corrispondente sarà interface Propriety { String getNome(); String getValue(); } Si consideri ora una classe Impiegato, con i campi principali nome(String) e stipendio(int). Supponiamo che Impigato renda disponibile all’ esterno una salaryPropriety, cioè una proprietà che abbia come nome il nome dell’ impiegato e come valore il suo stipendio. Supponiamo che dall’ esterno sia possibile richiedere tale proprietà con il metodo Propriety getSalaryPropriety();

11 classi locali a metodi : esempio
Come possiamo realizzare ció? Innanzi tutto notiamo una cosa importante: un oggetto di tipo SalarityPropriety appartiene esclusivamente a un impiegato; è pertanto conveniente cominciare a pensare di rendere SalarityPropriety una inner class di Impiegato(notare inoltre che in questa implementazione la classe SalarityPropriey sia priva di variabili proprie su cui chiamare getNome() e getValue(), poiché si hanno già a disposizione le variabili di Impiegato): class Impiegato { String nomeDip; double stipendio; private class SalarityPropriety implements Propriety { public String getNome() { return nome; } public String getVaue() { String val=“”; return val + stipendio; } } // altro… In realtà scopriamo che solo il metodo getSalarityPropriety() è interessato a tale classe (deve infatti eseguire l’ istruzione return new SalarityPropriety() ). Pertanto risulta più conveniente rendere SalarityPropriety classe locale di getSalarityPropriety():

12 classi locali a metodi : esempio(2)
public Propriety getSalarityPropriety() { class SalarityPropriety() { // classe locale public String getNome() { return nome; } public String getValue() { /* vedi pag. precedente */ } } return new SalarityPropriety(); In questo modo abbiamo reso il codice ancora piú compatto.

13 Inner class vs Local class
Entrambe possono accedere alle istanze della classe contenitrice (eventualmente con la notazione estesa NomeClasse.this) 2) Le classi locali hanno una minore visibilità: solo il metodo in cui sono scritte le può richiamare; per gli altri metodi della classe contenitrice sono invisibili 3) Maggiore compattezza del codice usando classi locali 4) Le classi locali possono accedere anche alle variabili locali del metodo in cui sono poste(non che ai parametri di esso), purchè tali varabili(o parametri) siano qualificate come final, e purchè la classe locale non possieda variabili omonime (che coprirebbero irrimediabilmente quelle locali; non è possibile infatti avere riferimenti a metodi). Il motivo per cui le variabili locali devono essere final è che le classi locali sono implementate creando in opportuni campi una copia di tali variabili(un po’ come quando si passa un parametro a una funzione) 5) Le classi locali non possono essere precedute dai modificatori come public o private, in quanto sono trattate come variabili locali

14 Classi anonime Riprendiamo l’ esempio della classe SalarityPrpriety vista prima: avevamo reso tale classe locale al metodo getSalaryPropriety: public Propriety getSalarityPropriety() { class SalarityPropriety() { // classe locale public String getNome() { return nome; } public String getValue() { /* vedi pag. precedente */ } } return new SalarityPropriety(); Osservando bene questo metodo notiamo che in realtà la classe SalarityPropriety non viene mai usata, se non per creare “al volo” e restituire un oggetto di tale classe: sarebbe più conciso restituire un oggetto di una nuova classe che implementi l’ interfaccia Propriety, ma senza preoccuparci di dare un nome a tale classe(che tanto non servirebbe, in quanto non visibile e quindi utilizzabile esternamente). In effetti ciò è possibile grazie alle classi anonime

15 classi anonime (2) public Propriety getSalaryPropriety() {
return new Propriety() { // classe anonima… //… che implementa l’ interfaccia Propriety String getNome() { return nome; } String getValue() { String val=“”; return val+stipendio; } } Il senso di questa sintassi è quello di creare un nuovo oggetto di una nuova classe priva di nome che implementi l’ interfaccia Propriety. Esternamente, tale oggetto sarà utilizzabile come un oggetto di tipo Propriety, e il binding dinamico garantisce che verranno usati i metodi get() e set() come definiti sopra.

16 La sintassi generale delle classi anonime è
new SupeType( <parametri di SuperType>) { // corpo della classe anonima } SuperType è una interfaccia(allora la classe anonima implementa l’ interfaccia) o una classe vera e propria(allora in questo caso la classe anonima estende tale classe). I parametri utilizzati durante la creazione della classe anonima sono forniti al costruttore della classe base, e non a quello della classe anonima . Questo perché una classe anonima non può avere un costruttore proprio; infatti il costruttore è un metodo che ha il nome della classe, ma una classe anonima non ha alcun nome(per definizione). Utilità delle classi anonime Le classi anonime sono in parte una buona idea, in quanto rendono il codice il più compatto possibile(c’ è meno “digitazione”); ma proprio tale compattezza può essere fonte di confusione. Conviene pertanto evitare, in nome della chiarezza l’ uso di tali classi se il codice da scrivere è di una certa lunghezza. Vediamo ora un uso un più reale delle classi anonime:

17 Nella programmazione a eventi si ha spesso la necessità di aggiungere a un componente
oggetti che implementino interfacce come ActionListener o WindowListener: infatti un oggetto di questo tipo è in grado di “reagire” a eventi che derivano dalla manipolazione di quel componente. Ad esempio se il componente è una finestra, la chiusura della stessa dovrà causare una istruzione di chiusura del programma(che altrimenti resterebbe caricato in memoria); è pertanto necessario aggiungergli, tramite il metodo addWindowListener(WindowListener listener) un oggetto che implementi tale interfaccia, e in particolare il metodo windowClosing(WindowEvent e),il metodo invocato alla chiusura della finestra. In realtà non è necessario implementare tutti i metodi di WindowListener, in quanto la classe WindowAdapter implementa già tali metodi, che sono tutti vuoti. Pertanto se siamo interessati a catturare solamente l’ evento “chiusura della finestra” è sufficiente creare una classe che estenda WindowAdapter e ridefinisca windowClosing() affinché chiuda l’ intero programma. Il corpo di tale metodo è solitamente molto breve, e consiste della sola istruzione System.exit(0). Poiché l’ unico uso esterno che viene fatto di un oggetto di tale classe estesa è quello di essere creato e passato come parametro del metodo addWindowListener del componente, è comodo in questo caso fare uso di una classe anonima che estenda WindowAdapter: Component finestra=new JFrame(); crea una finestra finestra.addWindowListener(new WindowAdapter() { // classe anonima che estende // WindowAdapter windowClosing(WindowEvent e) { System.exit(0); } // ridefinisce il metodo });

18 Approfondimenti Si parlerà qui brevemente dell’ implementazione delle inner class. Innanzitutto le i.c. sono una questione tipica del compilatore: il compilatore crea due file separati .class(il nome del file per la classe inner è nomebase$nomeinner.class; se si tratta di una classe anonima il nome è nomebase$n, con n numero progressivo), e la macchina virtuale li tratta allo stesso modo, senza fare alcuna distinzione fra loro. Il compilatore deve quindi trasformare la inner class in una classe normale: nel fare ciò la prima cosa da fare è mettere un riferimento all’ oggetto ospite, solitamente nominato this$0 (il campo outer visto nei lucidi precedenti); ma ciò non basta ancora: è necessario dare alla inner class il privilegio di accedere ai campi privati dell’ oggetto ospite. Ciò viene realizzato inserendo nella classe contenitore dei metodi statici (assolutamente invisibili al programmatore) di nome access$0(), access$1(), etc. Questi metodi contengono sempre un riferimento ad un oggetto di tipo classe contenitore che individua l’ oggetto a cui si vuole accedere, e sono usati dalla inner class per accedere (in lettura o scrittura) ai metodi privati della classe base. In altre parole hanno una funzione di get() e set() per le variabili private dell’ ogggetto contenitore. Facciamo un esempio: consideriamo la funzione getValue() della classe SalarityProprety : l’ istruzione return val+stipendio verrebbe tradotta come return val+access$0(this$0), dove access$0 sarà del tipo double acces$0(Impiegato i) { return stipendio; }

19 Questo fatto rappresenta un rischio per la sicurezza: infatti si potrebbe usare i metodi access$ per accedere a informazioni private dell’ oggetto base. Naturalmente poiché i nomi sorgente non possono contenere caratteri speciali non basta collocare tale chiamata in un programma java e compilare: tuttavia per chi ha familiarità con i file .class è possibile scrivere un file .class con istruzioni macchina in grado di richiamare tali metodi. Riepilogando, se una inner class accede a dati privati è possibile accedere a questi anche tramite altre classi aggiunte al package, ma ciò richiede abilità e determinazione: non è possibile per un programmatore ottenere incidentalmente l’ accesso; egli deve creare esplicitamente un file di classe per ottener tale scopo. RIFERIMENTI: Castroman, Horell,”JAVA, fondamenti”, MacGrowHill (52,00 euro) AUTORE: NOME: Manni Stefano, TEL: ; /* EOF !! */


Scaricare ppt "INNER CLASS Una Inner class è una classe la cui dichiarazione si trova all’ interno di un’ altra classe (La classe ospitante è detta top level): class."

Presentazioni simili


Annunci Google