Unità didattica 8.1) - Pagina 1
Unità didattica 8.2) - Pagina 2
Unità didattica 8.3) - Pagina 3
Unità didattica 8.4) - Pagina 4
4 - Unità didattica 8.4)
- Altri componenti di un’applicazione: classi
interne e anonime Nel modulo 2, abbiamo introdotto i principali
componenti di un’applicazione Java. Ci sono alcuni concetti che
volutamente non sono stati introdotti, quali le classi astratte, le
interfacce e le classi anonime. Di seguito introduciamo le classi
interne e le classi anonime, mentre il discorso sulle interfacce è
rimandato al prossimo modulo.
N.B.: i prossimi argomenti sono presentati per completezza, giacché
l’intento di questo testo è quello di spiegare tutti i concetti
fondamentali del linguaggio Java. Dobbiamo però avvertire il lettore
che un utilizzo Object Oriented del linguaggio non richiede
assolutamente l’impiego di classi interne e/o anonime.
N.B.: il lettore può tranquillamente saltare il prossimo paragrafo
se non è interessato alle ragioni "storiche" che hanno portato
all’introduzione delle classi anonime e delle classi interne nel
linguaggio.
- Classi interne: introduzione e storia
Quando nel 1995 la versione 1.0 di Java, fu introdotto al mondo
della programmazione, si parlava di linguaggio orientato agli
oggetti "puro". Ciò non era esatto. Un linguaggio orientato agli
oggetti puro, com ad esempio lo Smalltalk, non ha tipi di dati
primitivi ma solo classi da cui istanziare oggetti. Di conseguenza
non esistono operatori nella sintassi. Per esempio, per sommare due
numeri interi, dovremmo istanziare due oggetti dalla classe Integer,
ed invocare il metodo add() della classe Math, passandogli come
parametri i due oggetti. È facile intuire perché lo Smalltalk non
abbia avuto un clamoroso successo. Java vuole essere un linguaggio
orientato agli oggetti, ma anche semplice da apprendere. Di
conseguenza non ha eliminato i tipi di dati primitivi e gli
operatori. Ciononostante, il supporto che il nostro linguaggio offre
ai paradigmi della programmazione ad oggetti, come abbiamo avuto
modo di apprezzare, è notevole. Ricordiamo che l’object orientation
è nata come scienza che vuole imitare il mondo reale, giacché i
programmi, rappresentano un tentativo di simulare concetti fisici e
matematici importati dalla realtà che ci circonda. C’è però da
evidenziare un aspetto importante delle moderne applicazioni object
oriented. Solitamente dovremmo dividere un’applicazione in tre parti
distinte:
una parte rappresentata da ciò che è visibile all’utente (view),
ovvero l’interfaccia grafica (in inglese Gui: Graphical User
Interface)
una parte che rappresenta i dati su cui si basa l’applicazione
(Model)
una parte che gestisce la logica dell’applicazione (Controller).
Partizionare un’applicazione come descritto, implica notevoli
vantaggi per il programmatore. Per esempio, nel debug, semplifica la
ricerca dell’errore. Quanto appena riportato non è altro che una
banalizzazione di uno dei più importanti Design Pattern conosciuti,
noto come Model – View – Controller Pattern, o più brevemente, MVC
Pattern (per sapere cosa è un Design Pattern: http://hillside.net/patterns
). L’MVC propone questa soluzione architetturale, per motivi molto
profondi ed interessanti, e la soluzione è molto meno banale di
quanto si possa pensare. L’applicazione di questo modello, implica
che l’utente utilizzi l’applicazione, generando eventi (come la
pressione del bottone del mouse su di un bottone) sulla View, che
saranno gestiti dal Controller per l’accesso ai dati del Model. Il
lettore può immaginare che in questo modello le classi che
costituiscono il Model, la View ed il Controller, hanno dei ruoli
ben definiti. L’Object Orientation ovviamente supporta l’intero
processo d’implementazione dell’MVC, ma c’è un eccezione: la View.
Infatti, se un’applicazione rappresenta un’astrazione idealizzata
della realtà, l’interfaccia grafica non costituisce imitazione della
realtà. La Gui esiste solamente nel contesto dell’applicazione
stessa, "all’interno del monitor".
In tutto questo discorso si inseriscono le ragioni della nascita
delle classi interne e delle classi anonime nel linguaggio. Nella
versione 1.0 infatti, Java definiva un modello per la gestione degli
eventi delle interfacce grafiche, noto come "modello gerarchico".
Esso, effettivamente non distingueva in maniera netta, i ruoli delle
classi costituenti la View ed il Controller di un’applicazione. Di
conseguenza avevamo la possibilità di scrivere classi che astraevano
componenti grafici, che avevano anche la responsabilità di gestire
gli eventi che venivano generati da essi. Per esempio, un bottone di
un interfaccia grafica, poteva contenere il codice che doveva
gestire gli eventi di cui era sorgente. Una situazione del genere
non rendeva di certo giustizia alle regole dell’astrazione. Ma la
verità è che non esiste un’astrazione reale di un concetto che
risiede all’interno delle applicazioni!
In quel periodo, se da una parte erano schierati con Sun per Java,
grandi società come Netscape e IBM, dall’altra ce ne era un’altra,
con un potere ed un’influenza notevoli sul mondo informatico:
Microsoft. Ovviamente, un linguaggio indipendente dalla piattaforma,
non era (ed è) gradito al monopolista dei sistemi operativi. In quel
periodo provenirono attacchi da più fonti verso Java. Si mise in
dubbio addirittura che Java fosse un linguaggio object oriented! Lo
sforzo di Sun si concentrò allora nel risolvere ogni ambiguità,
riscrivendo una nuova libreria di classi (e di interfacce). Nella
versione 1.1, fu definito un nuovo modello di gestione degli eventi,
noto come "modello a delegazione". Questo nuovo modo per gestire gli
eventi, permette di rispettare ogni regola dell’object orientation.
Il problema nuovo, riguarda però la complessità di implementazione
del nuovo modello, che implica la scrittura di codice tutto sommato
superfluo. Infatti, un’applicazione che deve gestire eventi con la
filosofia della delegazione, richiede la visibilità da parte di più
classi, sui componenti grafici della gui. Ecco che allora
contemporaneamente alla nascita del modello a delegazione fu
definita anche una nuova struttura dati: la classe interna (Inner
Class).
- Classi interne: definizione
Una classe interna non è altro che una classe che viene definita
all’interno di un’altra classe. Per esempio:
class Outer
{
private String messaggio = "Nella classe ";
private void stampaMessaggio()
{
System.out.println(messaggio+"Esterna");
}
class Inner //Definizione della classe interna
{
public void metodo()
{
/*la classe interna accede in maniera
naturale ai membri
della classe che la contiene*/
System.out.println(messaggio+"Interna");
}
. . .
}
Il vantaggio di implementare una classe all’interno di un’altra,
riguarda principalmente il risparmio di codice. Infatti, la classe
interna ha accesso alle variabili di istanza della classe esterna.
- Classi interne: proprietà
N.B.: Le seguenti proprietà vengono riportate per completezza.
Tuttavia, non accenneremo al fine di utilizzare le classi interne in
situazioni tanto singolari. Dubitiamo fortemente, per esempio, che
il lettore a breve scadenza abbia l’esigenza di dichiarare una
classe interna statica!
Una classe interna:
deve avere un identificatore differente dalla classe che la contiene
si può utilizzare solo nello scope in cui è definita, a meno che non
si utilizzi la sintassi: nomeClasseEsterna.nomeClasseInterna
si può dichiarare anche all’interno di un metodo, ma in quel caso,
le variabili locali saranno accessibili solo se dichiarate final
ha accesso sia alle variabili d’istanza sia a quelle statiche della
classe in cui è dichiarata
se viene dichiarata statica diventa automaticamente una top-level
class (non sarà più interna)
solo se dichiarata statica può dichiarare membri statici
può essere dichiarata astratta
N.B.: i modificatori abstract e static, saranno argomenti del
prossimo modulo
N.B.: se compiliamo una classe che contiene una classe interna,
saranno creati due file: "nomeClasseEsterna.class" e
"nomeClasseEsterna$nomeClasseInterna.class".
- Classi anonime: definizione
Come le classi interne, le classi anonime sono state introdotte
successivamente alla nascita del linguaggio: nella versione 1.2. Le
classi anonime non sono altro che classi interne, ma senza nome.
Essendo classi interne, godono delle stesse proprietà, e sono
utilizzate per gli stessi scopi (gestione degli eventi). La
dichiarazione di una classe anonime, richiede anche l’istanza di un
suo oggetto, e l’esistenza di una sua superclasse di cui sfrutterà
il costruttore. Infatti se una classe non ha nome non può avere un
costruttore. La sintassi a prima vista può disorientare:
class Outer
{
private String messaggio = "Nella classe ";
private void stampaMessaggio()
{
System.out.println(messaggio+"Esterna");
}
//Definizione della classe anonima e sua istanza
ClasseEsistente ca = new ClasseEsistente() {
public void metodo()
{
System.out.println(messaggio+"Interna"); }
}; //Notare il ";"
. . .
}
//Superclasse della classe anonima
public class ClasseEsistente
{
. . .
}
N.B.: se compiliamo una classe che contiene una classe interna,
saranno creati due file: "nomeClasseEsterna.class" e
"nomeClasseEsterna$1.class". Ovviamente, se introduciamo una seconda
classe anonime, sarà creato anche il file
"nomeClasseEsterna$2.class", e così via.
Unità didattica 8.1) - Pagina 1
Unità didattica 8.2) - Pagina 2
Unità didattica 8.3) - Pagina 3
Unità didattica 8.4) - Pagina 4