martedì 16 novembre 2010

Top Level Class e Nested Class in Java

La Top Level Class è una classe la cui dichiarazione non risiede all'interno di un'altra classe o di un'interfaccia. Tutti i tipi che vengono dichiarati all'interno di una Top Level Class possono essere indicati con il termine nested.

Vi sono svariati modi in cui i tipi nested possono essere dichiarati all'interno di una Top Level Class. Ad esempio, tutti i tipi nested non static sono membri della classe contenitore. I tipi dichiarati static invece, non possono essere essere considerati membri della classe contenitore. La differenza fondamentale sta nel fatto che i membri sono instanziati al momento in cui viene istanziata la classe contenitore e sono fortemente accoppiati con essa. I tipi statici invece presentano un livello di accoppiamento più debole e non dipendono direttamente da un'istanza della classe contenitore, ma solo da un suo riferimento.

Fra i tipi permessi, vi è anche il tipo class. Infatti Java permette di dichiarare una classe all'interno di una Top Level Class. La classe dichiarata internamente è detta Nested Class. La Nested Class segue le regole di tutti gli altri tipi, infatti può essere dichiarata public, private, protected o package private e può essere static.

Una Static Nested Class può essere definita nel seguente modo:


public class OuterWithStaticNested {

private String outerField;

private static String OUTER_STATIC_FIELD = "Outer Static Field";

public OuterWithStaticNested(String outerField) {
this.outerField = outerField;
}

public String getOuterField() {
return outerField;
}

static class StaticInnerClass {

void print() {
System.out.println(OUTER_STATIC_FIELD);
System.out.println(new OuterWithStaticNested("Outer Field")
.getOuterField());
}

}

}

Dal punto di vista comportamentamentale la Static Nested Class è molto simile ad una Top Level Class. Non ha, infatti, accesso diretto a membri e metodi privati della classe che la contiene, e per utilizzarli, deve appoggiarsi ad un'istanza concreta della classe contenitore. Ha invece accesso diretto ai tipi statici, anche se privati. Questo implica un basso livello di accoppiamento fra la Top Level Class e la Static Nested Class, la quale può essere istanziata in maniera diretta, senza che ci sia bisogno di un'istanza della Top Level Class.

StaticNestedClass staticNestedClass = new OuterWithStaticNested.StaticNestedClass();

Le Inner Classes sono invece fortemente accoppiate con l Top Level Class in cui sono dichiarate. Hanno infatti accesso a tutti i membri e metodi privati della classe contenitore e possono essere a loro volta considerate come un membro di essa.


public class OuterWithInner {

private String outerField;

private static String OUTER_STATIC_FIELD = "Outer Static Field";

public OuterWithInner(String outerField) {
this.outerField = outerField;
}

class InnerClass {

void print() {
System.out.println(outerField);
System.out.println(OUTER_STATIC_FIELD);
}

}

}

A testimonianza di questo forte accoppiamento, la Inner Class può essere inizializzata esternamente alla Top Level Class solo a partire da una sua istanza concreta.

OuterWithInner outerClass = new OuterWithInner("Outer Field");
InnerClass innerClass = outerClass.new InnerClass();

Per questi motivi, una Inner Class non può contenere metodi o membri statici.

Fra le Nested Classes troviamo anche le Anonymous Inner Class, cioè classi che vengono dichiarate e istanziate nel punto in cui devono essere usate. Non hanno nome e non possono essere considerate come un membro della classe in cui sono dichiarate. Sono permesse in qualunque punto in cui è ammessa una espressione.

Possono essere dichiarate sia in un contesto statico che non statico, ma non possono avere membri statici.

Uso comune delle Anonymous è la creazione di oggetti funzionali (cioè istanze di classi senza stato, i cui metodi operano solo sugli oggetti passati nella signature) “on the fly”. Un tipico esempio è l'utilizzo del metodo

java.util.Collections.sort(List, Comparator)

dove spesso si implementa un Comparator "al volo" per indicare la logica di ordinamento degli elementi.

public class OuterWithAnonymous {

private List outerFields;

public OuterWithAnonymous(List outerFields) {
this.outerFields = outerFields;
}

public void sortOuterFieldsByFieldLenghtAsc() {
Collections.sort(outerFields, new Comparator() {

@Override
public int compare(String field1, String field2) {
if (field1.length() > field2.length()) {
return 1;
} else if (field1.length() == field2.length()) {
return 0;
} else {
return -1;
}
}

});
}
}

Altro caso d'uso frequente è per la creazione di oggetti process come Runnable, Thread o TimerTask.

Infine, il tipo meno comune di Nested Class è la Local Class. Le Local Classes sono dichiarate all'interno di un metodo, allo stesso modo di una variabile locale, e obbediscono alla stessa politica di scope.

public class OuterWithLocal {

private List outerFields;

public OuterWithLocal(List outerFields) {
this.outerFields = outerFields;
}

public void sortOuterFieldsByFieldLenghtAsc() {
Comparator stringComparator = new Comparator() {

@Override
public int compare(String field1, String field2) {
if (field1.length() > field2.length()) {
return 1;
} else if (field1.length() == field2.length()) {
return 0;
} else {
return -1;
}
}

};
Collections.sort(outerFields, stringComparator);
}
}

Come le Inner Classes, hanno nome e possono essere utilizzate più volte.Come le Anonimous Inner Classes, non possono avere membri statici e hanno un riferimento alla classe contenitore solo se sono dichiarate in un contesto non statico.


Le quattro tipoligie di Nested Classes hanno tutte caratteristiche diverse, che possono tradursi in svantaggi se non sfruttate al meglio. Se la Nested Class non deve avere accesso diretto a metodi e membri della classe contenitore, allora è preferibile che sia dichiarata statica, in modo da evitare che ogni istanza della Nested Class necessiti di un'istanza concreta della Top Level Class.

Le Anonimous Inner Classes e le Local classes devono essere brevi e molto semplici, in modo da non diminuire troppo la leggibilità del codice. Le Anonimous sono da preferire alle Local quando devono essere istanziate una sola volta, nel punto in cui devono essere eseguite. Le Local sono da preferire alle Inner se devono essere usate più volte in un unico metodo.

Nessun commento:

Posta un commento