2014-09-11 22 views
5

sto leggendo attraverso la seguente sezione nel tutorial Java: http://docs.oracle.com/javase/tutorial/java/generics/capture.htmlJava Generics jolly vs utilizzo tipizzati Generics

Si inizia dicendo che il seguente codice genera un errore a causa del fatto che una cattura non può essere convertito in un oggetto in modo che il metodo di set non può confermare che oggetto è di tipo capture # 1:

import java.util.List; 

public class WildcardError { 

    void foo(List<?> i) { 
     i.set(0, i.get(0)); 
    } 
} 

ho po capire il ragionamento alla base di questo. i.get restituisce un oggetto e il compilatore non può determinare se l'oggetto è di tipo capture n. 1 in modo che non possa corrispondere al secondo argomento in modo typesafe.

E poi consiglia di utilizzare il seguente codice per rendere questo metodo di lavoro:

public class WildcardFixed { 

    void foo(List<?> i) { 
     fooHelper(i); 
    } 


    // Helper method created so that the wildcard can be captured 
    // through type inference. 
    private <T> void fooHelper(List<T> l) { 
     l.set(0, l.get(0)); 
    } 

} 

ho po capire perché questo codice funziona così, in questo codice, l.get è garantito per essere di tipo T, in modo da esso può essere passato come argomento di tipo T.

Quello che non capisco è il motivo per cui non si potrebbe utilizzare un metodo come questo, senza alcun aiuto:

class GenericsTest { 
    static <K> void bar(List<K> l) { 
     l.set(0, l.get(l.size() - 1)); 
    } 

    public static void main(String[] args) { 
     List<Integer> lst = Arrays.asList(1, 2, 3, 4); 
     bar(lst); 
     System.out.println(lst); // [4, 3, 2, 4] 
    } 
} 

Ad esempio, se si intende utilizzare l'inferenza di tipo, perché non utilizzare solo generici espliciti digitati su un jolly e una funzione di supporto? C'è qualche vantaggio nell'usare i caratteri jolly in questo scenario? In quali casi preferiresti effettivamente utilizzare un carattere jolly su un generico digitato?

+0

Cosa significa "statico vuoto", secondo voi? –

+0

@Mike Per me, significa che K è un parametro di tipo che si applica solo all'ambito del metodo. Java usa l'inferenza di tipo per capire cosa K dovrebbe essere in fase di runtime. Nell'esempio mostrato, passo in un'istanza di List così il compilatore deduce K come intero. K può essere qualsiasi tipo di riferimento e i tipi di riferimento si estendono da Object, quindi considero K come un altro modo di scrivere esplicitamente in questo caso . – Shashank

+0

@Mike non lo so ... sembra essere lo stesso di un normale metodo vuoto. Non sapevo che K avesse qualcosa a che fare con il ritorno. Sto solo usando la sintassi da qui: http://docs.oracle.com/javase/tutorial/java/generics/methods.html – Shashank

risposta

1

primo atto che void foo(List<?> i) e void <K> foo(List<K> i) sia accettare la stessa identica serie di argomenti - supponendo che non specificare esplicitamente un K sul caso metodo generico, qualsiasi argomento che può essere passato a una firma funzione può essere passato al altro e viceversa. Quindi, per "codice esterno", entrambe le firme sono ugualmente utili.

Dato che sono equivalenti, quello con meno parametri di tipo è più semplice e deve essere sempre preferito. Quando si presenta un'API pubblica a un codice esterno, si dovrebbe sempre presentarla nella forma più semplice, avendo solo il minimo necessario per renderlo sicuro. Il tipo List<?> è sufficiente per esprimere che può prendere qualsiasi tipo di List, quindi è quello che dovremmo usare.

ci capita di utilizzare il parametro di tipo K internamente per risolvere alcuni problemi generici, ma questo è solo una sfortunata dettaglio interno di implementazione che codice esterno non ha bisogno di preoccuparsi. Pertanto, dovremmo nascondere questa bruttezza e avvolgerla in una firma di funzione più carina quando si effettua un'API pubblica.

Oltre ai fini dell'astrazione, un altro motivo per cui si desidera una firma particolare è se sostituisce un metodo di superclasse con quella firma. Non è possibile aggiungere parametri di tipo quando si esegue l'override.

0

Penso che l'esempio menzionato nel tutorial si riferisca alla gestione dei casi quando è necessario accettare i parametri con caratteri jolly non associati <?>. Questo può essere il caso quando si interagisce con il codice legacy o si estende una classe esistente con un tipo jolly di questo tipo.

Principalmente userete cose come <T extends U> o <? extends T> quando avete bisogno di flessibilità ma non volete sacrificare il controllo dei tipi.