2012-06-08 22 views
33

io sono un novizio in generico e la mia domanda è: che differenza tra le due funzioni:Differenza tra tipo generico e tipo jolly

funzione 1:

public static <E> void funct1 (List<E> list1) { 

} 

funzione 2:

public static void funct2(List<?> list) { 

} 

Grazie.

+1

Se String si riferisce a 'java.lang.String', penso che funct2 sia piuttosto ridondante nelle espressioni jolly, poiché' java.lang.String' è una classe finale e non può essere estesa. – nhahtdh

+1

Questo è strano perché tecnicamente non è possibile estendere String dal momento che è una classe finale. –

+0

tuttavia non vi è alcuna restrizione sull'uso della funzione 2: in quanto consente di sottoclassi di String (anche se impossibile) o della classe String stessa ... – ria

risposta

29

La prima firma dice: lista1 è una lista di Es.

La seconda firma indica: elenco è un elenco di istanze di qualche tipo, ma non conosciamo il tipo.

La differenza diventa evidente quando si cerca di modificare il metodo in modo che richiede un secondo argomento, che dovrebbe essere aggiunto all'elenco all'interno del metodo:

import java.util.List; 

public class Experiment { 
    public static <E> void funct1(final List<E> list1, final E something) { 
     list1.add(something); 
    } 

    public static void funct2(final List<?> list, final Object something) { 
     list.add(something); // does not compile 
    } 
} 

Il primo funziona bene. E non puoi cambiare il secondo argomento in qualcosa che verrà effettivamente compilato.

In realtà ho appena trovato una dimostrazione ancora più bello della differenza:

public class Experiment { 
    public static <E> void funct1(final List<E> list) { 
     list.add(list.get(0)); 
    } 

    public static void funct2(final List<?> list) { 
     list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!! 
    } 
} 

Sarebbe come il motivo per cui abbiamo bisogno <?> quando si limita solo ciò che possiamo fare con esso (come ha fatto @Babu_Reddy_H nei commenti) . Vedo i seguenti vantaggi della versione jolly:

  • Il chiamante deve conoscere di meno circa l'oggetto che passa, ad esempio se ho un Mappa di Liste:. Map<String, List<?>> posso passare i suoi valori alla funzione, senza specificando il tipo degli elementi della lista. Quindi

  • Se distribuisco oggetti parametrizzati in questo modo, limito attivamente ciò che le persone sanno di questi oggetti e cosa possono fare con esso (a patto che rimangano lontani da un casting non sicuro).

Questi due hanno senso quando li unisco: List<? extends T>. Ad esempio, prendere in considerazione un metodo List<T> merge(List<? extends T>, List<? extends T>), che unisce i due elenchi di input a un nuovo elenco di risultati. Certo, potresti introdurre altri due parametri di tipo, ma perché vorresti? Sarebbe finita di specificare le cose.

  • finalmente jolly può avere limiti inferiori, quindi con le liste si può fare il lavoro add metodo, mentre get non ti dà qualcosa di utile. Ovviamente questo fa scattare la domanda successiva: perché i generici non hanno limiti inferiori?

Per una più approfondita risposta vedere: When to use generic methods and when to use wild-card? e http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203

+0

Grazie, ho capito – Fio

+4

Vale anche la pena notare che non è solo su puts. Con un 'Elenco ', puoi scrivere 'E obj = list.get (0)', ma con 'Lista ' puoi solo scrivere 'Oggetto obj = list.get (0)'. – yshavit

+0

@Jens schauder. Entrambi ? e E consente solo ai metodi oggetto di invocare sull'oggetto. E uno può aggiungere all'elenco e non può fare lo stesso con l'elenco . Allora perché abbiamo bisogno della lista ? –

1

L'elenco come parametro dice che il parametro deve essere un elenco di elementi con qualsiasi tipo di oggetto. Inoltre, è possibile associare il parametro E per dichiarare riferimenti a voci di elenco all'interno del corpo della funzione.

L'elenco come tipo di parametro ha la stessa semantica, tranne per il fatto che non esiste un modo per dichiarare riferimenti agli elementi nell'elenco diverso da quello Object. Altri post danno ulteriori sottili differenze.

+0

scusa ho un errore nella mia domanda e ho modificato la domanda. – Fio

+0

@Gene puoi spiegare perché nel secondo parametro di funzione deve essere un elenco di oggetti che derivano da String. Poiché Parameter non sta definendo il suo tipo di oggetto "List list" quindi penso che possa contenere qualsiasi tipo di oggetto insieme a String. –

+0

Invece di dire "prima" o "seconda", includere il codice pertinente per evitare confusione (e modifiche all'OP). –

5

Generics rende la raccolta più sicura.

List<E>: E qui è il parametro di tipo, che può essere utilizzato per determinare il tipo di contenuto della lista, ma non c'era No modo per verificare quale fosse il contenuto durante la runtime.

Generics are checked only during compilation time. 

<? extends String>: Questo è stato appositamente costruire in java, per gestire il problema che era presso il tipo di parametro. "? extends String" significa questo elenco può avere

objects which IS-A String. 

Per esempio:

classe Animal classe Dog estende classe Animal Tiger si estende Animal

Quindi, utilizzando "public void go(ArrayList<Animal> a)" volontà NOT accept cane o Tiger come il suo contenuto, ma degli animali.

"public void go(ArrayList<? extends Animal> a)" è che cosa è necessario per fare il check ArrayList take in Dog and Tiger type.

per i riferimenti a Head First Java.

+0

posso usare solo? come mostrato nell'elenco precedente Elenco degli articoli ? allora quali tipi di oggetti è possibile memorizzare –

+0

"?" è usato per rendere la collezione in grado di prendere gli argomenti dal metodo chiamato che estende il tipo menzionato qui "". –

+0

Lista lista in questo esempio utente non ha definito il tipo ... allora che tipo di oggetto ci vorrà? –

1

La prima è una funzione che accetta un parametro che deve essere un elenco di elementi di tipo E.

il secondo tipo di esempio non è definito

List<?> list 

in modo da poter passare elenco di tutti i tipi di oggetti.

0

(Dal momento della modifica) Queste due funzioni hanno lo stesso effetto sul codice esterno: entrambe considerano come argomento lo List. Un jolly equivale a un parametro di tipo che viene utilizzato una sola volta.

1

Io di solito spiegare la differenza tra < E> e < ?> da un confronto con quantificazioni logiche, cioè quantificazione universale e quantificazione esistenziale.

  • corrisponde a "forall E, ..."
  • corrisponde a "esiste qualcosa (indicato con) tale che ...."

Pertanto, entrambe le seguenti dichiarazioni generiche metodo

function 1:

public static <E> void funct1 (List<E>; list1) { 

} 

function 2:

public static void funct2(List<?> list) { 

} 

significa che, per ogni tipo di classe E, abbiamo definire funct1 e, per alcuni esiste classe indicata da < ?>, dfine funct1.

0

In aggiunta a queste differenze accennato prima, c'è anche una differenza in più: è possibile impostare in modo esplicito gli argomenti di tipo per la chiamata del metodo generico:

List<Apple> apples = ... 
ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok 
           // with type parameters, even though the method has none 

ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple> 
            //     cannot be converted to List<Banana> 

(ClassName è il nome della classe che contiene i metodi.)

Problemi correlati