Generics Java: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
m Categorizzata
m Erasure: clean up
 
(53 versioni intermedie di 36 utenti non mostrate)
Riga 1:
{{w|torna a|ottobreJava 20065}}
 
Il [[Java 5|JDK 1.5]] ha introdotto alcune interessanti estensioni al [[LinguaggioJava (linguaggio di programmazione Java)|linguaggio Java]]. Una di questa è l'introduzione dei '''generics''' o '''tipi generici'''.
Un generics è uno strumento che permette la definizione di un tipo parametrizzato, che viene esplicitato successivamente in fase di compilazione secondo le necessità; i generics permettono di definire delle [[Astrazione (informatica)|astrazioni]] sui [[Tipo di dato|tipi di dati]] definiti nel linguaggio.
I generics permettono di definire delle astrazioni sui tipi di dati definiti nel linguaggio. L'esempio più comune del loro utilizzo è nella definizione/uso dei, cosiddetti, contenitori.
Prima dell'avvento del JDK 1.5 per poter gestire in modo trasparente tipi di dati differenti si doveva ricorrere al fatto che in [[Linguaggio di programmazione Java|Java]] ogni classe deriva in modo implicito dalla classe ''Object''; per esempio se si doveva implementare una linked list il codice era seguente:
 
== Caratteristiche ==
List myIntList = new LinkedList();
Vi sono svariati vantaggi nell'uso dei generics:
myIntList.add(new Integer(0));
*Fornisce una migliore gestione del '''[[type check]]ing''' durante la compilazione;
*Evita il casting da ''Object. I.e.'';
*Evita errori dovuti a casting impropri
 
Invece di utilizzare (codice che potrebbe generare un errore di casting):
 
<syntaxhighlight lang="java">
String title=((String) words.get(i)).toUppercase();
</syntaxhighlight>
o più correttamente per evitare errori
<syntaxhighlight lang="java">
Object o=words.get(i);
String title="";
if(o instanceof String)
title=((String) o.get(i)).toUppercase();
</syntaxhighlight>
 
verrà utilizzato:
 
<syntaxhighlight lang="java">
String title=words.get(i).toUppercase();
</syntaxhighlight>
 
Vi sono però anche degli svantaggi:
 
Si definisce:
 
<syntaxhighlight lang="java">
List<String> words=new ArrayList<String>();
</syntaxhighlight>
 
invece di:
 
<syntaxhighlight lang="java">
List words=new ArrayList();
</syntaxhighlight>
 
L'esempio più comune del loro utilizzo è nella definizione/uso dei cosiddetti ''[[container (informatica)|contenitori]]''. Prima dell'uscita del JDK 1.5 per poter gestire in modo trasparente tipi di dati differenti si doveva ricorrere al fatto che in Java ogni [[Classe (informatica)|classe]] deriva in modo implicito dalla classe ''Object''. Per esempio se si doveva implementare una [[lista concatenata]] il codice era il seguente:
 
<syntaxhighlight lang="java">
List myIntList = new LinkedList();
myIntList.add(new Integer(0));
</syntaxhighlight>
 
e invece per recuperare l'elemento appena inserito si doveva scrivere
 
<syntaxhighlight lang="java">
Integer x = (Integer) myIntList.iterator().next();
Integer x = (Integer) myIntList.iterator().next();
</syntaxhighlight>
 
Si noti il [[Conversione di tipo|cast]] a ''[[Integer]]'' necessario poiché ''myIntList'' in realtà lavora su oggetti ''Object''.
Dall'introduzione del JDK 1.5 invece è possibile utilizzare un codice come il seguente:
 
<syntaxhighlight lang="java">
List<Integer> myIntList = new LinkedList<Integer>();
List<Integer> myIntList.add( = new LinkedList<Integer>(0));
myIntList.add(new Integer(0));
</syntaxhighlight>
 
dove viene esplicitamente espresso che la lista ''myIntList'' lavorerà solo su oggetti di tipo ''Integer''. Per recuperare l'elemento appena inserito il codice è il seguente:
 
<syntaxhighlight lang="java">
Integer x = myIntList.iterator().next();
Integer x = myIntList.iterator().next();
</syntaxhighlight>
 
Si noti che ora il cast non è più necessario visto che la lista è di interi.
 
== Implementazione ==
Java 5 non ha esteso il linguaggio [[bytecode]] per implementare i generics. Questo vuol dire che i generics sono in realtà solo dei costrutti sintattici, emulati a livello di bytecode tramite il solito meccanismo della classe Object (descritto sopra).<ref>[http://www.artima.com/intv/generics2.html Generics in C#, Java, and C<!-- Titolo generato automaticamente -->]</ref> Dichiarare
<syntaxhighlight lang="java">
List<Integer> myIntList = new LinkedList<Integer>();
</syntaxhighlight>
equivale a livello di codice a dichiarare
<syntaxhighlight lang="java">
List myIntList = new LinkedList(); // Lista di Object
</syntaxhighlight>
e ad eseguire implicitamente le conversioni ''Object->Integer'' e ''Integer->Object'' per leggere e scrivere gli elementi.
 
I generics hanno quindi eliminato i problemi riguardanti la tipizzazione; adesso gli elementi della lista devono essere ''Integer'' e non (per esempio) ''String'' e tale controllo è eseguito a tempo di compilazione.
 
== Erasure ==
L{{'}}'''Erasure''' è il processo che converte il programma codificato con i generici nella forma senza di essi, che rispecchia più da vicino il [[bytecode]] prodotto. Questo termine non è del tutto corretto in quanto vengono sì rimossi i generici, ma vengono anche aggiunti i cast.
L'aggiunta di questi cast non è esplicita e il linguaggio di progetto fornisce la '''Cast-iron guarantee''': ossia il cast implicito aggiunto alla compilazione dei generici: non può mai fallire. Questa è una regola che si applica per il codice che non presenta ''unchecked warnings''.
I vantaggi dell'implementazione via '''Erasure''', sono:
* mantenere le cose semplici senza aggiunta di dettagli o altro;
* mantenere le cose piccole ad esempio con una sola implementazione di List;
* semplificare l'evoluzione, la stessa libreria può essere accessibile da un codice generico e da codici legacy.
 
Se un elemento Y deriva da un elemento X, non si può dire che una collezione di elementi di Y derivi dalla collezione di elementi X, perché in generale ciò risulta un'operazione impossibile; solo alcune di questo potrebbero essere anche sicuri, soprattutto per ciò che riguarda gli array e la lettura, ma ciò non è vero in generale.
 
Consideriamo la classe generica ''LinkedList <T>'':
prendiamo due sue '''istanziazioni''': ''LinkedList <Number>'' e ''LinkedList<Integer>''.
Sono due tipi differenti, incompatibili fra loro, anche se ''Integer'' estende ''Number''; situazione in contrasto a quella che si verifica negli array, dove ''Integer[]'' è un sottotipo di ''Number[]''.
Per verificarlo, creiamo due liste:
 
<syntaxhighlight lang="java">
LinkedList<Number> l1 = new LinkedList<Number>();
LinkedList<Integer> l2 = new LinkedList<Integer>();
</syntaxhighlight>
 
E consideriamo i due possibili assegnamenti l1 = l2 e l2 = l1; si
ottiene in ogni caso errore perché
''LinkedList<Integer>'' non estende ''LinkedList<Number>''.
 
Gli assegnamenti in precedenza sono impossibili perché viene violato '''il principio di sostituzione''': Ad una variabile di un certo tipo può essere assegnato un valore di ogni sottotipo; un metodo con un argomento di un certo tipo può essere chiamato con un argomento di ogni sottotipo.
 
<syntaxhighlight lang="java">
List<Number> numbers = new ArrayList<Number>();
numbers.add(2);
numbers.add(3.14d);
assert numbers.toString().equals("[2, 3.14]");
</syntaxhighlight>
 
Qui il principio vale fra ''List'' e ''ArrayList'' e fra ''Number'' e ''Integer Double'' in maniera rispettiva.
''List<Integer>'' invece '''non è un sottotipo''' di ''List<Number>'' in quanto viene violato nuovamente '''il principio di sostituzione''', ad esempio:
<syntaxhighlight lang="java">
List<Integer> integers = Arrays.asList(1, 2);
List<Number> numbers = integers; // non compila
numbers.add(3.14d);
assert integers.toString().equals("[1, 2,3.14]");
</syntaxhighlight>
 
== Tipi parametrici varianti (Wildcard) ==
Non può esistere una compatibilità generale fra i tipi parametrici. Se si è alla ricerca della compatibilità, bisogna prendere in considerazione casi specifici e tipi di parametri di singoli metodi. Quindi, alla normale notazione dei tipi generici List<T>, usata per creare oggetti, si affianca una nuova notazione, pensata per esprimere i tipi accettabili come parametri in singoli metodi.
 
Si parla quindi di tipi parametrici varianti, in [[Java (linguaggio di programmazione)|Java]] detti '''wildcard'''.
 
Posto che la notazione ''List<T>'' denota il normale tipo generico, si introducono le seguenti '''notazioni wildcard''':
*tipo '''covariante''' ''List<? extends T>'': cattura le proprietà dei ''List<X>'' in cui ''X'' estende ''T''; si usa per specificare tipi che possono essere solo letti.
*tipo '''controvariante''' ''List<? super T>'': cattura le proprietà dei ''List<X>'' in cui ''X'' è esteso da ''T''; si usa per specificare tipi che possono essere solo scritti.
*tipo '''bivariante''' ''List<?>'': cattura tutti i ''List<T>'' senza distinzioni; si usa per specificare i tipi che ''non'' consentono né letture né scritture.
 
== Definizione di una classe generica ==
Ecco un esempio di classe generics
<syntaxhighlight lang="java">
public class Gen<X,Y> {
 
private final X var1;
private final Y var2;
 
public Gen(X x,Y y) {
var1 = x;
var2 = y;
}
 
public X getVar1() {
return var1;
}
 
public Y getVar2() {
return var2;
}
public String toString() {
return "(" + var1 + ", " + var2 + ")";
}
}
</syntaxhighlight>
 
Questa classe non serve a nulla da sola, deve essere quindi utilizzata un'altra struttura, come nell'esempio successivo:
 
<syntaxhighlight lang="java">
 
Gen<String, String> esempio1 = new Gen<String, String>("esempio", "uno");
Gen<String, Integer> esempio2 = new Gen<String, Integer>("esempio", 2);
 
System.out.println("primo esempio: " + esempio1);
System.out.println("secondo esempio: " + esempio2);
 
</syntaxhighlight>
 
==Note==
<references/>
 
{{Portale|informatica}}
 
[[Categoria:Java]]
[[Categoria:Programmazione generica]]