Tipi generici

Un tipo generico (generic type) è una classe o un'interfaccia parametrizzata rispetto ai tipi. Vediamo un esempio con una classe Box.

Una semplice classe Box :

public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

Formato per definire una classe generica:

class name<T1, T2, ..., Tn> { /* ... */ }

Le sezione dei type parameters, delimitata da <>, segue il nome della classe. Essa specifica i type parameters (anche detti type variables) T1, T2, .....Tn.

La generic class declaration di Box diventa:

/**
 * Generic version of the Box class.
 * @param <T> the type of the value being boxed
 */
public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Come si può vedere, tutte le occorrenze di Object sono sostituite da T . Un tipo variabile può essere qualsiasi tipo non primitivo: qualsiasi tipo classe, tipo interface, tipo array, o anche un altro tipo parametrico.

Per riferirsi a un generic Box nel codice, bisogna eseguire una generic type invocation, che sostituisce il tipo generico T con il tipo concreto, ad esempio Integer:

Box<Integer>  integerBox;

Un altro esempio, di definizione di tipo parametrico:

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

    private K key;
    private V value;

    public OrderedPair(K key, V value) {
	     this.key = key;
	     this.value = value;
    }

    public K getKey()	{ return key; }
    public V getValue() { return value; }
}

Ora l'istanziazione del tipo parametrico:

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String>  p2 = new OrderedPair<String, String>("hello", "world");

Si può sostituire al parametro di un tipo parametrico (es: T ) con un altro tipo parametrizzato ad esempio List<Integer>, ad esempio:

OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));

Generic methods

Generic methods sono metodi che introducono anche loro tipo parametrici. Sono simili alla dichiarazionedi un tipo parametrico, ma lo scope del tipo parametrico è limitata al metodo dove è dichiarato. Sono parmessi metodi parametrici, di tipo statico e non statico, così come costruttori generici.

La sintassi per un metodo generico include la lista det tipi parametrici prima del tipo di ritorno del metodo.

La classe Util contiene un metodo parametrico compare tra due Pair object:

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

public class Pair<K, V> {

    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

Per invocare il metodo:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);

Il tipo è stato provvisto esplicitamente (<Integer, String>). Generalment si può anche evitare, il compilatore riesce a inferire i tipi cge sono necessari:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);

Last updated