I caratteri jolly nei generici di Java limitano o aumentano la flessibilità?

4

Ho letto molte fonti su caratteri jolly e generici Java. Anche se ho visto molte spiegazioni e risposte, nessuna sembra corretta.

La domanda è molto semplice: le wilcard in Java generici aumentano la flessibilità?

La mia comprensione è "No". I caratteri jolly nei generics di Java (wilcards) in realtà riducono la flessibilità. Quando ho visto per la prima volta e utilizzato il meccanismo dei generici Java, raramente ho mai usato i caratteri jolly. Molto spesso ho visto e utilizzato la forma <T> abc(T xt) . Un giorno ho ricevuto una domanda di prova su super e in seguito ho iniziato a esaminare i caratteri jolly più da vicino.

Molte fonti e in particolare: Effective Java 2nd fornisce un esempio come segue per mostrare come i caratteri jolly aumentano la flessibilità.

public class Stack<E> {
    ...
    public void pushAll(List<E> list) {...}
    ...
}

I libri continuano e dice che dal momento che dovremmo essere in grado di utilizzare List di qualsiasi cosa che estende E come argomento a pushAll(List<E> list) , questo non è abbastanza flessibile. Pertanto, il libro suggerisce di cambiare la firma del metodo in pushAll(List<? extends E> list) . Ora se E è Number allora possiamo anche usare List<Integer> come argomento per pushAll(List<? extends Number> list) . E che abbiamo aumentato la flessibilità (dell'API).

La mia comprensione è che l'altra soluzione non è l'uso di caratteri jolly ma l'uso della digitazione normale. In tal caso, se cambiamo la firma del metodo in

public <T extends E> void pushAll(List<T> list)

raggiungeremo la stessa cosa che la versione con caratteri jolly vuole ottenere. E ora nel corpo del metodo, non solo possiamo leggere da list , possiamo anche scrivere in esso. Se usiamo la versione con caratteri jolly, non possiamo scrivere in quell'elenco alcun valore diverso da null . Pertanto, in questa visualizzazione, i caratteri jolly effettivamente diminuiscono la flessibilità, non aumentano.

Tutto sommato, mi sembra che i caratteri jolly siano effettivamente utilizzati per ridurre la flessibilità in modo che <? extends X> scoraggi le persone dallo scrivere nell'oggetto mentre <? super X> scoraggia le persone dalla lettura dell'oggetto (provalo e otterrai solo Object come tipo del valore restituito). Il seguente esempio illustra il caso <? super X> .

public void popAll(List<? super E> list) {
    Object x = list.get(0) // Only get Object as type here, reading is discouraged
}

Possiamo cambiare la firma in public <T extends E> void popAll(List<T> list) . Ora possiamo scrivere o leggere come vogliamo. Non ci sono restrizioni.

Quindi riassumendo: tutti gli usi di caratteri jolly possono essere sostituiti dal parametro digitato nel modo <T extends E> (quindi ci può essere non è necessario per la parola chiave super ). Usando i caratteri jolly, perdiamo la flessibilità nella capacità di leggere e scrivere nell'oggetto. Pertanto, i caratteri jolly diminuiscono la flessibilità.

È quindi vero che i caratteri jolly in Java riducono effettivamente la flessibilità? Altrimenti, in che modo il mio ragionamento è difettoso?

[ La mia domanda è difettosa in quanto manca i casi menzionati da @Doval nel commento sotto la sua risposta. ]

    
posta InformedA 05.02.2015 - 04:38
fonte

2 risposte

7

My understanding is that the other solution is not to use wildcards but to use the regular typing. In that case, if we change the method signature to (...) And now in the body of the method, not only we can read from list, we can also write into it. If we use the the wildcard version, we cannot write into that list any value other than null. So in this view, wildcards actually decrease flexibility, not increase.

Il problema è che stai assumendo che la maggiore flessibilità sia per la persona che scrive la funzione; è per la persona che usa la funzione. Maggiore è la flessibilità di implementazione, maggiore è la flessibilità di guadagno del chiamante. Le restrizioni all'implementazione sono garanzie per il chiamante.

Caso in questione, quando si modifica pushAll(List<E>) in pushAll(List<? extends E>) , l'implementatore non può più presumere di conoscere il tipo preciso memorizzato nell'elenco, ed è per questo che non è possibile scrivere in modo sicuro nell'elenco. Ma a causa di ciò, il chiamante è ora libero di passare qualsiasi elenco i cui elementi sono un sottotipo di E , mentre prima poteva solo passare gli elenchi di E .

Hai affermato che se cambi pushAll in <T extends E> void pushAll(List<T>) puoi scrivere nella lista, ma in questo caso non è vero. Non sai quale classe T sia in anticipo, quindi non puoi creare una T da inserire nell'elenco. L'unico modo in cui potresti aggiungere una T alla lista è se il chiamante te l'ha dato. Ecco perché Effective Java sostiene che è necessario sostituire i parametri di tipo con i caratteri jolly se compaiono solo una volta nel metodo; lo scopo principale dei parametri di tipo è quello di consentire di dare un nome sconosciuto a un nome in modo da poterlo fare più volte.

All in all, it looks to me that wildcards are actually used to decrease flexibility so that discourages people from writing into the object while <? super X> discourages people from reading the object (try it and you will get only Object as type of the returned value). (...) We can change the signature to public <T extends E> void popAll(List<T> list). Now we can write or read as we want. There is no restriction.

Non puoi semplicemente scambiare super per extends . Supponiamo che tu abbia un Stack<Number> . Che cosa succederà se passi un List<Integer> a popAll e lo stack tenta di inserire un Double nell'elenco? Allo stesso modo, supponi di passare un List<Object> a pushAll ; cosa succederà se l'elenco ha un String e si prova ad inserirlo in una pila di numeri? Cose brutte. Ecco perché utilizzi ? extends E quando vuoi leggere dalla raccolta e ? super E quando hai intenzione di scrivere su di essa; è l'unico modo sicuro per farlo. (C'è un mnemonico per ricordare quale usare: PECS . Il produttore si estende, il consumatore è super.)

Inoltre, i parametri di tipo non possono avere limiti inferiori (non puoi dire T super SomeClass ), quindi sei obbligato a utilizzare un carattere jolly per super .

    
risposta data 06.02.2015 - 15:23
fonte
3

L'articolo 28 del manuale (Effective Java 2e) è 'Usa caratteri jolly limitati per aumentare la flessibilità dell'API'.

Sì, i caratteri jolly limitati aumentano la flessibilità rispetto ai tipi di messaggi senza limiti, ma non hanno la capacità di esprimere alcuni concetti che possono essere raggiunti con limiti di tipo non jolly.

Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code.

Quindi quando un tipo appare nel tipo restituito, non deve essere un jolly. Inoltre, è preferibile non utilizzare i caratteri jolly se un tipo appare più volte nell'intestazione del tipo.

Quindi, perché mai vorresti dei caratteri jolly limitati?

There is a duality between type parameters and wildcards, and many methods can be declared using one or the other. [...] As a rule, if a type parameter appears only once in a method declaration, replace it with a wildcard.

Possono essere più semplici da leggere per le persone che usano la tua API.

Tuttavia, Effictive Java 2E è stato scritto per Java5, che era un po 'indietro, e Java 7 è venuto con miglioramenti all'inferenza per i parametri di tipo e l'operatore diamante. Forse in questa età più moderna dovrebbero essere preferibili i parametri di tipo.

    
risposta data 05.02.2015 - 14:35
fonte

Leggi altre domande sui tag