Differenze tra le versioni di "Java/Generics"

Correggo tag deprecato
m (Bot: sostituzione tag obsoleti)
(Correggo tag deprecato)
 
 
Un tipo che dichiara uno o più ''type parameters'' è detto ''tipo generico''. Ad esempio:
<sourcesyntaxhighlight lang="Java">
/**
* @param E Il tipo degli elementi contenuti nella collezione.
... // altri metodi
}
</syntaxhighlight>
</source>
 
In questo caso, <code>Collezione</code> è un tipo generico, ed <code>E</code> è il nome dell'unico ''type parameter''. La scrittura <code>Collezione</code> è chiamata ''raw type''; la scrittura <code>Collezione&lt;java.lang.String></code> identifica una ''collezione di stringhe'' e si chiama ''parametrized type''.
Ad esempio, si consideri un metodo che estrae un elemento a caso da un array il cui ''component type'' non è noto a priori. Prima dell'introduzione dei ''generics'', il metodo doveva accettare un parametro <code>Object[]</code> e avere tipo di ritorno <code>Object</code>. Con i ''generics'', si può definire una ''type variable'' <code>E</code> ed usarla sia come ''component type'' dell'array, sia come tipo di ritorno del metodo.
 
<sourcesyntaxhighlight lang="Java">
public class Utilities {
public <E> E unElementoACaso(E[] elementi) {
// ... altri metodi
}
</syntaxhighlight>
</source>
 
In questo esempio, il metodo
<sourcesyntaxhighlight lang="Java">
public E unElementoACaso(E[] array)
</syntaxhighlight>
</source>
è stato scritto in base al ''type parameter'' <code>E</code>. Una sintassi analoga vale per i costruttori.
 
 
Il primo caso è quello utilizzato più di frequente, e consiste nell'usare la stessa sintassi che invoca un metodo non generico. Ad esempio:
<sourcesyntaxhighlight lang="Java">
public void stampaRecordACaso(Record[] records) {
Utilities utilities = new Utilities();
stampaRecord(r);
}
</syntaxhighlight>
</source>
 
Il secondo caso può essere usato per rendere il codice più chiaro, e a volte è richiesto dal compilatore in caso di ambiguità.
<sourcesyntaxhighlight lang="Java">
public void stampaRecordACaso(Record[] records) {
Utilities utilities = new Utilities();
stampaRecord(r);
}
</syntaxhighlight>
</source>
 
== Convenzioni di nomenclatura ==
È possibile delimitare il tipo che può essere passato come parametro alla classe tramite l'utilizzo di ''extends'' o ''super'' e dell'ereditarietà delle classi. Infatti, poiché i tipi che vengono passati sono delle classi, esse hanno una superclasse e una sottoclasse, attraverso esse è possibile istruire il compilatore affinché accetti classi che rientrino entro il limite superiore o il limite inferiore imposto, tramite ''extends'' o ''super'' rispettivamente. Si considerino ad esempio le seguenti classi:
 
<sourcesyntaxhighlight lang=java>
class ClassePadre {}
class ClasseFiglio extends ClassePadre{}
class ClasseFiglio2 extends ClasseFiglio{}
</syntaxhighlight>
</source>
 
Ora, se si dovesse creare una classe generic che, per possibili problemi con il codice scritto al suo interno, debba accettare solo classi che ereditano da una certa classe, ad esempio: da ''ClasseFiglio'', la classe Generic dovrà avere questo aspetto:
 
<sourcesyntaxhighlight lang=java>
class ClasseGeneric<T extends ClasseFiglio>{
[...]
}
</syntaxhighlight>
</source>
 
in questo modo ogni tentativo di passare una classe che non erediti da ClasseFiglio, come ClassePadre, non farà compilare.<br />
Mentre se si vuole che una classe Generic accetti tutte le classi superclassi di una classe, la classe Generic dovrà delimitare con ''super'', prendendo d'esempio le classi prima presentate, avrà il seguente aspetto:
 
<sourcesyntaxhighlight lang=java>
class ClasseGeneric<T super ClasseFiglio>{
[...]
}
</syntaxhighlight>
</source>
 
in questo modo ogni tentativo di passare alla classe generic una sottoclasse della classe ClasseFiglio, come ClasseFiglio2, non farà compilare.