Non so come capire il tipo di jolly in Java

5

Sto leggendo il Core Java (9a edizione) di Cay S. Horstmann e Gary Cornell. Dopo aver fatto uno sforzo, non riesco ancora a capire il ? super Manager . Ecco alcuni materiali relativi a questa domanda.

public class Pair<T> {
    private T first;
    private T second;

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public void setSecond(T second) {
        this.second = second;
    }

    public T getFrist() {
        return this.first;
    }

    public T getSecond() {
        return this.second;
    }
}

Il Manager eredita da Employee e Executive erediti da Manager . Coppia ha metodi come segue:

void setFirst(? super Manager)
? super Manager getFirst()

Da ? super Manager denota qualsiasi supertipo di Manager , perché non riesco a chiamare il metodo setFirst con Employee (è ovvio che Employee è il supertipo di Manager), ma solo con type Manager o un sottotipo come Executive (ma Executive è il sottotipo di Manager, non un supertipo di Manager)?

    
posta Lucas Li 15.06.2014 - 04:47
fonte

2 risposte

2

Si può leggerlo e ripeterlo più e più volte, come è già successo molte volte. In un commento alla tua domanda, Philipp ha già collegato a una domanda su Stackoverflow che tenta di rispondere . Per me, la spiegazione di Zio Bob ha fatto il trucco.

Scrive (accanto a molte altre cose buone):

A List<? super Shape> is a list into which you may add Shapes. Another way to think of this is that the type <? super T> is an unknown type that T can substitute for. You may have to read that several times before you understand it. I certainly did. So here's yet another way to think about it. The type <? super T> is a type that T is derived from. It is a base class of T. Clearly if D is derived from B then you can put D instances into a List<B>. Likewise, since T derives from <? super T>, you can put T instances into a List<? super T>. Follow? No? Neither did I for awhile. It takes a bit of getting used to.

Che cosa significa alla fine è che la tua ipotesi di poter mettere i super-tipi di Manager nella tua List<? super Manager> è sbagliata. Questo non è ciò che si intende qui. La parola chiave super risolve il problema di non essere in grado di inserire elementi in luoghi generici mentre sei riuscito a ottenere un po 'di out (con extends ).

Immagina due coppie, una che estenda Manager, una che "la superi":

Pair<? extends Manager> subPair = new Pair<Manager>();
Pair<? super Manager> superPair = new Pair<Manager>();

Ciò che puoi mai fare, è inserire un Employee in uno di questi:

subPair.setFirst(new Employee());
superPair.setFirst(new Employee());

Se vuoi manager, i dipendenti non saranno mai abbastanza. Ed è peggio: non puoi nemmeno inserire un Executive nel tuo subPair , anche se Executive extends Manager . Non compilerà neanche:

subPair.setFirst(new Executive());

Modifica Perché? Bene, il tuo generico subPair fa sempre riferimento ad alcuni "espliciti" Pair , Pair<Manager> in questo caso. Potrebbe anche essere riferito a un Pair<CoManager> (con CoManager extends Manager ). Java non conoscerà il tipo esplicito effettivamente utilizzato e quindi non può consentire le inserzioni.

Ma super viene in soccorso. Con super puoi inserire Manager e i loro sottotipi in Pair . Ed è ciò che super è per: Inserimento.

superPair.setFirst(new Executive());

Modifica: Qual è il motivo per cui funziona? Mentre extends ti garantisce un tipo un po ' che implementa (o deriva da) Manager , super ti garantisce qualsiasi tale tipo. Quindi, mentre con extends ottieni un tipo specifico con (almeno) l'interfaccia Manager , con super puoi inserire qualsiasi tipo con Manager interfaccia.

E per l'immagine più grande, suggerisco davvero di leggere l'articolo di Uncle Bob.

    
risposta data 15.06.2014 - 12:26
fonte
0

La differenza sembra essere lo scopo della tua variabile.

Se si utilizza una variabile di tipo Pair da leggere, si utilizza Pair<? extends Manager> In questo caso, quando si chiama un metodo di coppia che restituisce qualcosa parametrizzato, si avrà il vantaggio di type-safe. Ma quasi non puoi scrivere nulla, sempre un errore di compilazione.

D'altra parte, se usi una variabile di tipo Pair per scrivere, allora usi %codice%. In questo caso, quando chiami un metodo di tipo Pair<? super Manager> che riceve una variabile parametrizzata di tipo (o sottotipo) di Pair<> , hai il tipo safe.

Onestamente, non vedo la differenza tra Manager e Pair<? super Manager> eccetto che quando usi Pair<Manager>  i metodi di lettura in Pair < > restituisce un oggetto java generico (cioè non sicuro per la lettura, il tipo di compilatore scoraggia la lettura in questo caso)

Penso che vederlo come letto e scrivere in questo modo sia più facile.

Ora torna al motivo per cui non puoi scrivere un oggetto Pair<? super Manager> nel tuo Employee . Questo perché Pair<? super Manager> è in realtà un tipo che estende un supertipo sconosciuto di <? super Manager> . E poiché è sconosciuto, non sai se Manager sia effettivamente un sottotipo di quel tipo sconosciuto.

Ancora più concreto: supponiamo che Employee implementi anche un'interfaccia Manager . Quindi IManager può essere un oggetto <? super Manager> e ora IManager non è una sostituzione valida di Employee (perché non è un sottotipo di IManager ). In questo caso, qualsiasi sottotipo di IManager può essere una sostituzione poiché sarà sottotipo di qualsiasi supertipo sconosciuto di Manager . Puoi chiamare Manager qui.

    
risposta data 15.06.2014 - 08:34
fonte

Leggi altre domande sui tag