Le risposte precedenti spiegano i parametri di tipo (T, E, ecc.), Ma non spiegano il carattere jolly "?" O le differenze tra loro, quindi lo affronterò.
Innanzitutto, per essere chiari: i parametri jolly e type non sono gli stessi. Laddove i parametri di tipo definiscono una sorta di variabile (ad esempio, T) che rappresenta il tipo per un ambito, il carattere jolly no: il carattere jolly definisce solo un set di tipi consentiti che è possibile utilizzare per un tipo generico. Senza alcun limite ( extends
o super
), il carattere jolly significa "usa qui qualsiasi tipo".
Il carattere jolly si trova sempre tra parentesi angolari e ha significato solo nel contesto di un tipo generico:
public void foo(List<?> listOfAnyType) {...} // pass a List of any type
mai
public <?> ? bar(? someType) {...} // error. Must use type params here
o
public class MyGeneric ? { // error
public ? getFoo() { ... } // error
...
}
Diventa più confuso dove si sovrappongono. Per esempio:
List<T> fooList; // A list which will be of type T, when T is chosen.
// Requires T was defined above in this scope
List<?> barList; // A list of some type, decided elsewhere. You can do
// this anywhere, no T required.
Vi è molta sovrapposizione in ciò che è possibile con le definizioni dei metodi. I seguenti sono, funzionalmente, identici:
public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething) {...}
Quindi, se ci sono sovrapposizioni, perché usare l'una o l'altra? A volte, onestamente è solo stile: alcune persone lo dicono se non lo fai bisogno di un tipo di parametro, dovresti usare un carattere jolly solo per rendere il codice più semplice / più leggibile. Una delle principali differenze che ho spiegato sopra: i parametri di tipo definiscono una variabile di tipo (ad esempio, T) che puoi usare altrove nell'ambito; il carattere jolly no. Altrimenti, ci sono due grandi differenze tra i parametri di tipo e il carattere jolly:
I parametri di tipo possono avere più classi limite; il carattere jolly non può:
public class Foo <T extends Comparable<T> & Cloneable> {...}
Il carattere jolly può avere limiti inferiori; i parametri di tipo non possono:
public void bar(List<? super Integer> list) {...}
In quanto sopra List<? super Integer>
definisce Integer
come limite inferiore il carattere jolly, il che significa che il tipo Elenco deve essere Intero o un super-tipo di Numero intero. Il limite del tipo generico va oltre ciò che voglio trattare in dettaglio. In breve, ti permette di definire quali tipi può essere un tipo generico. Ciò consente di trattare i farmaci genericamente polimorficamente. Ad esempio con:
public void foo(List<? extends Number> numbers) {...}
È possibile passare un List<Integer>
, List<Float>
,List<Byte>
, ecc per numbers
. Senza il limite di tipi, questo non funzionerà, ecco come sono generici.
Infine, ecco una definizione del metodo che utilizza il carattere jolly per fare qualcosa che non penso tu possa fare in nessun altro modo:
public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
numberSuper.add(elem);
}
numberSuper
può essere un Elenco di numeri o qualsiasi supertipo di Numero (ad esempio, List<Object>
) e elem
deve essere Numero o qualsiasi sottotipo. Con tutti i limiti, il compilatore può essere certo che .add()
is typesafe.