2010-06-18 19 views
12

Vedere Java Enum definition e Why in java enum is declared as Enum<E extends Enum<E>> per la discussione generale. Qui mi piacerebbe imparare che cosa esattamente si sarebbe rotto (non Typesafe più, o che richiedono ulteriori calchi ecc) che se la classe Enum è stato definito comeChe cosa sarebbe diversa se in Java dichiarazione Enum non ha avuto la parte ricorsiva

public class Enum<E extends Enum> 

sto usando questo codice per testare le mie idee:

interface MyComparable<T> { 
    int myCompare(T o); 
} 

class MyEnum<E extends MyEnum> implements MyComparable<E> { 
    public int myCompare(E o) { return -1; } 
} 

class FirstEnum extends MyEnum<FirstEnum> {} 

class SecondEnum extends MyEnum<SecondEnum> {} 

Con esso non sono stato in grado di trovare alcun vantaggio in questo caso esatto.

PS. il fatto che io non sono autorizzato a fare

class ThirdEnum extends MyEnum<SecondEnum> {} 

quando MyEnum viene definito con la ricorsione è
a) non rilevante, perché con le enumerazioni reali non si è autorizzati a farlo solo perché non è possibile estendere enum
b) non vero - pls provalo in un compilatore e vedi che è in grado di compilare senza errori

PPS. Sono sempre più propenso a credere che la risposta corretta sarebbe "nulla cambierebbe se togli la parte ricorsiva" - ma non ci posso credere.

risposta

1

Credo che una ragione convincente per farlo è che rende il codice nella classe MyEnum più typesafe.

Si consideri che la parte ricorsiva fa una cosa del genere possibile:

class MyEnum<E extends MyEnum<E>> { 
    private Thing<E> util1() { return someObject } 
    private void util2(E e) {} 
    public int method(E o) { 
     Thing<E> thingy = o.util1(); 
     // You can call the util1 method on o and get a type safe return element. 
     E o1 = // I don't care how you get a parametrized E object. 
     o.util2(o1); 
     // You can call the util2 method with a typesafe parameter. 
    } 
} 

In breve, che ricorsività consente di inserire i metodi typesafe nella classe Enum che è possibile chiamare su ogni elemento E, e queste chiamate saranno typesafe.

+1

solo una piccola nota, 'o.util2 (this)' non verrà compilato poiché questo è considerato MyEnum non E. È possibile aggiungere un secondo parametro 'E o1' e chiamare' o.util2 (o1) 'sebbene per lo stesso effetto: si compilerà con 'E estende MyEnum ', ma non con 'E estende MyEnum ' –

+0

Grazie, ho corretto l'errore. –

+0

grazie! quel codice infatti è contrassegnato come non sicuro se si rimuove la ricorsione! Conoscete qualche uso reale di quella funzionalità in jdk? – atamur

1

Considerare Enum<E>.compareTo(E other).

That:

  • ha bisogno di lavorare con E piuttosto che enum in modo che non si tenta di confrontare un valore enum con un valore da un enum diversa
  • deve essere in grado di ottenere il numero ordinale valore dell'enum, tramite il metodo ordinal()dichiarato su Enum.

Come proponesti di realizzare quel lavoro senza il vincolo attuale?

Questo è solo il primo che ho trovato ... Sono sicuro che ce ne sono molti altri. Fondamentalmente è un modo per dire: "Non dovresti provare a trattare tutti gli enum come equivalente l'uno con l'altro ... un enum è un insieme chiuso in sé, ma tutte le enumerazioni condividono certe proprietà."

+0

comparePer vedere il mio qn. In questo modo funziona esattamente come dici tu. Senza alcuna ricorsione. Neanche un problema con ordinal: potresti fornire un codice che si rompe/non è tipicamente digitato? – atamur

+0

@atamur: Non avevo notato il bit di tipo non elaborato nel tuo esempio (hai modificato nei primi cinque minuti di richiesta? Forse l'ho semplicemente letto male). Il fatto che tu abbia bisogno di un tipo immediato immediato mi rende nervoso, ad essere onesto ... –

+0

Se l'ordinale è la ragione, allora 'classe Enum >' sarebbe sufficiente. – newacct

2

Beh, prima di tutto sarebbe lamentarsi utilizzando il tipo di prima, ma si poteva fare:

public class Enum<E extends Enum<?>> 

per lo stesso effetto.

Inoltre, con questo tipo di generici si potrebbe fare qualcosa di simile:

class FirstEnum extends MyEnum<SecondEnum> { 
} 

class SecondEnum extends MyEnum<FirstEnum> { 
} 

che a me sembra che potrebbe portare a un sacco di guai. Più esattamente non puoi confrontare un enum di tipo FirstEnum con un enum dello stesso tipo, devi confrontarlo con un enum dell'altro tipo, il che è davvero fastidioso se hai un List<FirstEnum> che vuoi ordinare.L'esempio non verrà compilato se ho impostato E anziché ? poiché SecondEnum non è del tipo E extends MyEnum<E> (ciò porterebbe all'ereditarietà circolare). Funzionerà comunque se FirstEnum extends MyEnum<FirstEnum> (il che significherebbe che SecondEnum è una classe figlia di FirstEnum - normale ereditarietà gerarchica).

+0

Il tipo raw sembra interessante, ma javac non emette alcun avviso non elaborato se effettivamente lo provi. pls vedere il mio ps nel qn. Ci sono 2 punti perché questo non è rilevante. – atamur

+1

Sto usando eclipse e avvisa del tipo non elaborato (per non parlare del fatto che il sole ha detto che non dovremmo più usare i tipi non elaborati). Forse javac richiede qualche tipo di opzione attivata per lamentarsi di questo? –

+1

Inoltre, il secondo punto di ps non è vero per il mio esempio, se imposto la classe su 'public MyEnum >' non viene compilato. Per quanto riguarda il primo punto, penso che potrebbe essere in grado di generare il file .class in modo che abbia le firme del metodo come se le classi fossero scritte in questo modo (usando un compilatore non standard) anche se javac non ti lasciava compilare ' public enum FirstEnum estende MyEnum 'o qualcosa di simile. –

0

Se non si dispone del parametro di tipo generico, non sarà possibile estendere Comparable<T extends Comparable> per il sottotipo specifico di enum che è stato creato.

Si potrebbe ignorare questo, e creare il proprio tipo MyEnum che si comporta più o meno lo stesso, ma senza la restrizione che diversi MyEnum non sono comparabili:

public abstract class MyEnum implements Comparable<MyEnum> 
{ 
    private final int ordinal; 

    protected MyEnum (int ordinal) 
    { 
     this.ordinal = ordinal; 
    } 

    public int ordinal() 
    { 
     return ordinal ; 
    } 

    public int compareTo (MyEnum other) 
    { 
     return ordinal - other.ordinal; // ignore overflow for now 
    } 

    public boolean equals (Object other) { 
     return ordinal == ((MyEnum)other).ordinal; 
    } 

    public int hashCode() { 
     return ordinal; 
    } 
} 

Questo si comporta più o meno allo stesso modo di un enum farebbe per le operazioni definite, ma invece di essere un tipo generico, l'implementazione sicura obbedisce allo LSP - gli oggetti di sottoclassi differenti di MyEnum sono paragonabili o uguali tra loro se hanno lo stesso valore ordinale.

public static class EnumA extends MyEnum 
{ 
    private EnumA (int ordinal) { super (ordinal); } 
    public static final EnumA a = new EnumA (0); 
    public static final EnumA b = new EnumA (1); 
} 

public static class EnumB extends MyEnum 
{ 
    private EnumB (int ordinal) { super (ordinal); } 
    public static final EnumB x = new EnumB (0); 
    public static final EnumB y = new EnumB (1); 
} 

public static void main (String...args) 
{ 
    System.out.println (EnumA.a.compareTo (EnumB.x)); 
    System.out.println (EnumA.a.equals (EnumB.x)); 
    System.out.println (EnumA.a.compareTo (EnumB.y)); 
    System.out.println (EnumA.a.equals (EnumB.y)); 
} 

In questo caso, se non ignorare equals, si perde la convenzione che implica x.comparesTo(y)=0x.equals(y); se si esegue l'override di equals, ci sono casi in cui x.equals(y) non implica x == y (come per gli altri oggetti valore), mentre per le enumerazioni Java entrambi i test di uguaglianza producono lo stesso risultato.

+0

Ho paura che tu non abbia capito il mio qn. Se guardi il mio codice, MyEnum utilizza i generici, ma non il bit ricorsivo. – atamur

0
`X extends Enum<Y>` 

sarebbe illegale. che è uguale a. il compilatore può avere regole speciali con enum, ma perché non avere una dichiarazione di tipo perfetta, se possibile?

Problemi correlati