Questi due esempi sono equivalenti e, di fatto, verranno compilati con lo stesso bytecode.
Esistono due modi in cui l'aggiunta di un tipo generico limitato a un metodo come nel tuo primo esempio farà qualsiasi cosa.
Passando il parametro type a un altro tipo
Queste due firme del metodo finiscono per essere le stesse nel codice byte, ma il compilatore applica 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, è consentito quanto segue 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 la raccolta di animali, mentre il primo consente la raccolta di qualsiasi oggetto assegnabile all'animale (ad es. Sottotipi). Nota che se questo elenco fosse un elenco di animali che contenevano un gatto, entrambi i metodi lo accetterebbero: il problema è la specifica generica della raccolta, non ciò che contiene effettivamente.
Restituzione di oggetti
L'altra volta è importante restituire oggetti. Supponiamo che esistesse il seguente metodo:
public static <T extends Animal> T feed(T animal) {
animal.eat();
return animal;
}
Saresti in grado di fare quanto segue con esso:
Cat c1 = new Cat();
Cat c2 = feed(c1);
Mentre questo è un esempio inventato, ci sono casi in cui ha senso. Senza generici, il metodo dovrebbe tornare Animal
e sarebbe necessario aggiungere il tipo casting per farlo funzionare (che è ciò che il compilatore aggiunge comunque al codice byte dietro le quinte).
addAnimals(List<Animal>)
e ad aggiungere un elenco di gatti!