2012-09-10 23 views
16

Nel mio ultimo question (grazie a tutti che mi rispondono), ho appreso la differenza tra List<Object> e List<?>.Java: caratteri jolly nuovamente

Tuttavia non riesco ancora a vedere l'utilità dei caratteri jolly.

Ho due ArrayList s:

ArrayList<Integer> li = new ArrayList<Integer>(Arrays.asList(1,2,3)); 
ArrayList<String> ls = new ArrayList<String>(Arrays.asList("one","two","three")); 

Ora, guardate i due blocchi di codice qui sotto:

static void printList(ArrayList<?> list) 
{ 
    for (Object elem: list) 
     System.out.print(elem + " "); 
    System.out.println(); 
} 

e

static <T> void printList(ArrayList<T> list) 
{ 
    for (T elem: list) 
     System.out.print(elem + " "); 
    System.out.println(); 
} 

Quando chiamo:

printList(li); 
printList(ls); 

Entrambi i metodi restituiscono l'output:

1 2 3 
one two three 

Tuttavia la seconda soluzione, nel ciclo for, invece di Object s che utilizzo tipi parametrizzati (molto più elegante credo).

Quindi, la domanda principale rimane: Perché abbiamo bisogno di caratteri jolly?

+0

jolly sono state fatte proprio alla fine del lavoro di generici. Per citare Josh Bloch parlando di chiusure. "Semplicemente non possiamo permetterci altri caratteri jolly". Penso che non pensi molto neanche a loro. Ive usa solo? quando sono limitati. ad es. RNJ

risposta

2

Utilizzando caratteri jolly, il codice è di tipo types. In

List myList; 

è possibile aggiungere qualsiasi oggetto desiderato. Ma in:

List<?> myList; 

è possibile aggiungere solo un nulla.

È necessario utilizzare solo Elenco, senza parametro di tipo, quando è necessaria la classe (List.class) o quando è necessario verificare il tipo (istanza di Elenco).

1

Perché

puoi modfify lista in caso di <T>list.add((T) new Object());

, ma non è possibile modificare l'elenco in caso di ? che assicura che la lista non ottiene modificato. Il compilatore genera un errore se si aggiunge qualcosa di diverso da null.

Collection<?> c = new ArrayList<String>(); 
c.add(new Object()); // Compile time error 

Dal momento che non sappiamo cosa il tipo di elemento di C sta per, non possiamo aggiungere oggetti ad esso. Il metodo add() accetta gli argomenti di tipo E, il tipo di elemento della raccolta. Quando il parametro del tipo attuale è?, Indica un tipo sconosciuto. Qualsiasi parametro che passiamo per aggiungere dovrebbe essere un sottotipo di questo tipo sconosciuto. Dato che non sappiamo di che tipo si tratti, non possiamo passare nulla. La sola eccezione è null, che è un membro di ogni tipo.

D'altra parte, dato un List<?>, possiamo chiamare get() e utilizzare il risultato. Il tipo di risultato è un tipo sconosciuto, ma sappiamo sempre che si tratta di un oggetto.E 'quindi sicuro per assegnare il risultato di get() a una variabile di tipo Object o passarla come parametro in cui il tipo di oggetto si prevede

Maggiori informazioni sul WildCards

+1

quindi la tua risposta è "l'uso di un parametro di tipo ti consente di forzare qualcosa di ridicolo e volare di fronte al sistema di tipi"? – newacct

7

Nel Java Generics Tutorial si dice:

Collection<?> c = new ArrayList<String>(); 
c.add(new Object()); // Compile time error 

Dal momento che non sappiamo cosa il tipo di elemento di C sta per, non possiamo aggiungere oggetti ad esso. Il metodo add() accetta gli argomenti di tipo E, il tipo di elemento della raccolta. Quando il parametro del tipo attuale è?, Indica un tipo sconosciuto. Qualsiasi parametro che passiamo per aggiungere dovrebbe essere un sottotipo di questo tipo sconosciuto. Dato che non sappiamo di che tipo si tratti, non possiamo passare nulla. La sola eccezione è null, che è un membro di ogni tipo.

D'altra parte, dato un elenco, possiamo chiamare get() e utilizzare il risultato. Il tipo di risultato è un tipo sconosciuto, ma sappiamo sempre che si tratta di un oggetto. È quindi sicuro assegnare il risultato di get() a una variabile di tipo Object o passarlo come parametro in cui è previsto il tipo Object.

Quindi avere un tipo di carattere jolly assicura che non possiamo add() a elenco, con l'eccezione di null. Se chiami solo get() nella lista, sai che è almeno un oggetto.

+0

Le spieghi, ma non esagerare con casi d'uso reali. Sicuramente ci sono altre strade per bloccare una chiamata 'add()' che non necessita di caratteri jolly. – Pureferret

4

Diciamo che avete questa gerarchia di classe:

Fruit 
Apple extends Fruit 
Orange extends Fruit 

... e alcune liste di ogni tipo di frutta:

List<Apple> apples ; e List<Orange> oranges ;

Ora si vuole un List che può riferirsi a qualcuno di questi elenchi.

Così si dichiara List<? extends Fruit> fruits ;

Non è possibile utilizzare una variabile di tipo qui.

11

Se la domanda è "utilità di jolly":

jolly sono utili quando è necessaria una conoscenza parziale sul parametro tipo. "Conoscenza parziale" è implementata dai limiti superiore e inferiore (? Super T o? Estende T); se si utilizza solo il carattere jolly non associato (?) si intende alcuna conoscenza, e non è possibile vedere dove i caratteri jolly sono davvero utili.

I caratteri jolly possono essere utilizzati in composizione con i parametri di tipo, per creare relazioni tra il tipo di parametri del metodo, tipo di ritorno e tipi di eccezione. Così un esempio di jolly utile è

class ListManager<T> { 
    public void add(T item, List<? super T> list) { 
     [... some useful operation ...] 
     list.add(item); 
    } 
} 

public class Main { 
    public static void main(String[] args) { 
     List<Object> list = new ArrayList<Object>(); 
     Integer item = 10; 
     ListManager<Integer> manager = new ListManager<Integer>(); 
     manager.add(item, list); 
    } 
} 

Il metodo "ListManager.add()" crea una relazione tra il tipo di "lista" e il tipo di "voce". L'operazione su "lista" è sempre di tipo sicuro, ma è possibile utilizzare il metodo "aggiungi" con l'elenco di diversi tipi di parametri: abbiamo usato il vincolo minimo sul parametro "elenco".

(see also jls7 documentation)

2

Hai ragione. Tecnicamente, ogni volta che vedi un carattere jolly al primo livello nel tipo di parametro, puoi creare una nuova variabile di tipo per esso e usare quella variabile di tipo al posto di quel carattere jolly, e funzionerebbe allo stesso modo.

Tuttavia, direi che è molto meno elegante rispetto all'utilizzo di un carattere jolly. Una variabile di tipo è utile per esprimere i vincoli tra diverse espressioni, come quando ci sono due parametri con la stessa variabile di tipo, o tra un parametro e un tipo di ritorno. Una variabile di tipo usata in un solo punto del parametro è una specie di spreco, che era esattamente ciò per cui i caratteri jolly sono destinati: una variabile di tipo "anonimo" che non è necessaria in nessun altro luogo. Quando hai solo bisogno di esprimere "un po 'di tipo", senza aver bisogno di vincolarlo con qualcos'altro, perché ingombrare la tua firma del metodo con variabili di tipo bonus?

Ci sono anche altri usi di caratteri jolly che non possono essere sostituiti con variabili di tipo. Ad esempio, prendere in considerazione un metodo public List<?> foo(). Restituisce una sorta di elenco, ma non si conosce un elenco di cosa (ed è pericoloso aggiungere qualcosa). Non c'è modo di esprimere questo usando le variabili di tipo senza caratteri jolly.

Inoltre, considera i caratteri jolly nei parametri di tipo nidificati: potresti avere una variabile di tipo List<List<?>>, ovvero un elenco eterogeneo i cui elementi possono essere elenchi di cose diverse. Anche questo non può essere espresso senza l'utilizzo di caratteri jolly.

+0

+1 per dare quella che sembra essere la sola risposta (non solo link) che risolve la domanda di '' rispetto a un parametro di tipo illimitato '' usato in un solo punto nella firma ("Una variabile di tipo usata in solo un posto nel parametro è una specie di spreco ") invece di ripetere che cos'è un jolly. –

1

Uno dei migliori utilizzi per i caratteri jolly che ho trovato sono nidificazioni complesse di tipi generici.

Un semplice esempio con un elenco di Coppia < T, E >, il carattere jolly rende il codice più breve e più leggibile poiché tutti possono vedere che sono interessato solo al valore booleano.

ArrayList<Pair<Boolean, OneOrOtherLongClassName>> pairList = ...; 
    for(Pair<Boolean,?> p:pairList) 
    { 
     if(p.first)count++; 
    } 
+0

dovrebbe essere 'Pair.First()'? – Pureferret

+0

@Pureferret no, con convenzioni di denominazione java che sarebbero un 'getFirst()' e tendo a scrivere Pair senza metodi get/set poiché è mutabile o i membri sono definitivi. – josefx

+0

Uh, allora come funziona? Classe – Pureferret

Problemi correlati