Java implementa tutte le quattro forme di polimorfismo
Poliformismo per inclusione
Si parla di polimorfismo per inclusione quando del codice scritto nei termini della classe/tipo A può essere utilizzato sostituendo all’oggetto di tipo A un altro oggetto di tipo B, per il fatto che è possibile considerare B un sottotipo di A. In Java, classe/sottoclasse permette di realizzare il polimorfismo per inclusione (Non bisogna però confondere la relazione classe/sottoclasse con la relazione tipo/sottotipo!)anche l’uso di interfacce permette di realizzare il polimorfismo per inclusione. Come abbiamo detto nelle lezioni precedenti tutte le classi discendono da Object, una variabile di classe Object potrà essere assegnata a qualsiasi oggetto, di conseguenza un metodo con parametri di classe Object potrà dunque accettare oggetti di qualunque tipo, nello stesso modo è possibile restituire un oggetto di classe Object per mantenere la massima genericità, in realtà è sempre garantita la compilazione ma non il corretto funzionamento!
Poliformismo per inclusione: Interfacce
Scrivere il codice in termini di interfacce è un metodo efficace di sfruttare il polimorfismo per inclusione in Java, lavorando in termini di Object si mantiene la genericità a scapito dell’espressività: gli oggetti potranno usare esclusivamente i servizi della classe Object, viceversa, utilizzare una classe base limita nella pratica la genericità del codice, “appesantita” dall’implementazione. Utilizzando un’interfaccia si potranno utilizzare tutti i metodi definiti dalla stessa (oltre ai metodi della classe Object!)
In generale, una struttura con alla base una o più interfacce e una classe astratta permette di sfruttare al meglio riuso e la genericità, es:
Poliformismo universale: Parametrico
I generics sono lo strumento fornito da Java per realizzare il polimorfismo parametrico essi permettono di utilizzare un parametro nella definizione di metodi/classi, il cui tipo viene determinato in fase di instanziazione, Il comportamento e le funzionalità dei generics sono simili a quelle dei template in C++ .
Es
List<Integer> myIntList = new LinkedList();
myIntList.add(new Integer(0));
Integer i =myIntList.getFirst();
[/code]
La sintassi per la definizione di una classe generica è la seguente:
modificatore_di_accesso class NomeClasse<E> {
modificatore_di_accesso tipo_restituito
nomeMetodo(E par1, …);
…
}
Esempio
public class ArrayList<E> {
void add(E x);
…
}
[/code]
Istanziare classi generiche
Quando viene istanziato un oggetto, è possibile indicare il tipo: tutte le occorrenze dei parametri formali di tipo sono sostituiti dal parametro specificato
List<Integer> al = new ArrayList<Integer>();
Piccolo approfondimento sui generics
Il tipo generico è quasi completamente gestito in tempo di compilazione, generando un bytecode unico che non dipende dai tipi parametrici, inoltre un tipo generico istanziato su un tipo funziona per inclusione anche con i sottotipi.
- Una collezione di Object potrà contenere oggetti di qualunque tipo
- Una collezione di Object non è superasse di una collezione con parametri più specifici
Es
[code lang=”java”]</pre>
<div title="Page 16">
<div title="Page 16">
<div>
<div>
<pre>List <Object> l = new ArrayList<Object>();
l.add(new String());//OK!
l = new ArrayList<String>();//NO!</pre>
</div>
</div>
</div>
</div>
<pre>
[/code]
[code lang=”java”]
<span style="font-family: Consolas, Monaco, monospace; font-size: 12px; line-height: 18px;">List <?> l = new ArrayList<String>();//OK</span></pre>
<div title="Page 19">
<div>
<div>
<pre>l = new ArrayList<Integer>();//OK!</pre>
</div>
</div>
</div>
<pre>[/code]
void moo(List <? extends Number> l){
l = new LinkedList <Double>();//OK!!
l = new LinkedList <Integer>();//OK!!
l = new LinkedList <Number>();//OK!!
l = new LinkedList <String>();//NO!!
}
[/code]
Metodi Generici
La sintassi generica è la seguente:
(…, [tipoGen<T> tipoG,] [T parG,][T[] arrG,] … )
Esempio
public class C {
static <T> Collection<T> arrayToCollection(T[] a){ Collection<T> c = new ArrayList();
for (T o : a) {
c.add(o);
}
return c;
}
public static void main(String[] args) {
Collection<String> c =arrayToCollection(args);
}
}
[/code]
Object[] oa = new Object[100];
Collection<Object> = C.fromArrayToCollection(oa);
String[] sa = new String[100];
Collection<String> cs = C.fromArrayToCollection(cs);
[/code]
Metodi Generici e Wildcards
Quando usare i metodi generici e quando i wildcards?
- Quando si vuole permettere ad un metodo di poter avere una varietà di tipi di argomenti attuali si usano i wildcards
- Quando si vogliono esprimere dipendenze tra i tipi di uno o più argomenti di un metodo e/o per il suo tipo restituito si usano i metodi generici