Questi due esempi sono equivalenti e infatti verranno compilati allo stesso bytecode.
Ci sono due modi in cui l'aggiunta di un tipo generico limitato a un metodo come nel tuo primo esempio farà qualsiasi cosa.
Passaggio del parametro type a un altro tipo
Queste due firme di metodo finiscono per essere le stesse nel codice byte, ma il compilatore impone la sicurezza del tipo:
public static <T extends Animal> void addAnimals(Collection<T> animals)
public static void addAnimals(Collection<Animal> animals)
Nel primo caso, è consentito solo un Collection
(o sottotipo) di Animal
. Nel secondo caso, è consentito un Collection
(o sottotipo) con un tipo generico di Animal
o un sottotipo.
Ad esempio, il seguente è permesso nel primo metodo ma non nel secondo:
List<Cat> cats = new ArrayList<Cat>();
cats.add(new Cat());
addAnimals(cats);
Il motivo è che il secondo consente solo collezioni di animali, mentre il primo consente raccolte di qualsiasi oggetto che è assegnabile ad animali (cioè sottotipi). Nota che se questa lista fosse un elenco di animali che contenevano un gatto, entrambi i metodi lo accetterebbero: il problema è la specifica generica della collezione, non ciò che in realtà contiene.
Restituzione di oggetti
L'altra volta che conta è con la restituzione degli oggetti. Supponiamo che il seguente metodo esistesse:
public static <T extends Animal> T feed(T animal) {
animal.eat();
return animal;
}
Sarai in grado di eseguire le seguenti operazioni:
Cat c1 = new Cat();
Cat c2 = feed(c1);
Sebbene questo sia un esempio forzato, ci sono casi in cui ha senso. Senza generici, il metodo dovrebbe restituire Animal
e sarà necessario aggiungere cast di tipo per farlo funzionare (che è ciò che il compilatore aggiunge al codice byte comunque dietro le quinte).