2010-11-10 19 views
7

Ho un sistema di istanze di oggetto che contengono un riferimento a un oggetto definizione. Ho una classe di primo livello per ogni albero di ereditarietà. L'oggetto istanza ha un riferimento generico alla classe di definizione corrispondente.Perché questo generico non riconosce il limite della superclasse (Java)?

Utilizzando i generici nel getter, una sottoclasse dell'oggetto di livello superiore può ottenere il giusto tipo di definizione senza eseguire il cast. Tuttavia, una sottoclasse astratta che è ancora una volta una sottoclasse non può:

class Def { } 

abstract class Animal<D extends Def> { 
    D def; 
    D getDef() { return def; } 
} 

class CatDef extends Def { } 
class Cat extends Animal<CatDef> { } 

abstract class BearDef extends Def { } 
abstract class Bear<D extends BearDef> extends Animal<D> { } 

class BlackBearDef extends BearDef { } 
class BlackBear extends Bear<BlackBearDef> { } 

class AnimalDefTest { 
    public static void main (String... args) { 
     Cat cat = new Cat(); 
     CatDef catDef = cat.getDef(); // CatDef works fine 

     Bear bear = new BlackBear(); 
     BearDef bearDef = bear.getDef(); // Error: Expected Def not BearDef? Why??? 
     BearDef bearDef2 = ((Animal<BearDef>)bear).getDef(); // Works 
    } 
} 

Perché getDef richiedono un Bear essere gettato a (Animal<BearDef>) per ottenere un BearDef? L'orso è definitivamente definito come extends Animal<? extends BearDef>.

[Modifica] Ancora più strano, se cambio la linea di classe dell'orso:

abstract class Bear<D extends BearDef> extends Animal<BearDef> { } 

(In questo caso D è inutilizzato ed è irrilevante) è ancora non funziona. Cancellare D e la seguente riga risolve l'errore nel codice di cui sopra (ma non mi aiuta fare quello che devo fare con le definizioni sottoclasse):

abstract class Bear extends Animal<BearDef> { } 

risposta

11

Si sta utilizzando il tipo grezzo Bear quando in realtà dovrebbe essere parametrizzato con un tipo di BearDef. Se hai scritto

Bear<BlackBearDef> bear = new BlackBear(); 

o

Bear<?> bear = new BlackBear(); 

che sarebbe funziona bene.

Un'altra cosa: io non sono sicuro di come si intende utilizzare questo, ma sembra probabile a me che si sarebbe bene solo facendo questo, invece:

abstract class Bear extends Animal<BearDef> {} 

class BlackBear extends Bear { 
    // make use of covariant return type to make BlackBear return correct def 
    @Override 
    BlackBearDef getDef() { ... } 
} 

Il motivo per pensare questo è che se si desidera un Bear il cui metodo getDef() restituisce un BlackBearDef, il riferimento Bear dovrebbe essere parametrizzato come Bear<BlackBearDef>. In quel caso (per il tuo esempio comunque) sai bene che si tratta di un BlackBear dal tipo dichiarato, quindi potresti anche riferirti ad esso come BlackBear. Detto questo, ovviamente non è sempre così semplice e la tua situazione attuale potrebbe non permetterlo.

+2

+1: Beat me ad esso. – Powerlord

+0

+1 anche da me. –

+0

Quella * specie di * ha senso, ma dal momento che sa che è * almeno * un BearDef, non dovrebbe essenzialmente assumere ciò che si ha nella seconda linea? – Nicole

3

Ciò funzionerà anche:

Bear<?> bear = new BlackBear(); 
Problemi correlati