2010-01-13 18 views
7

Erlaubt Java etwas so gutes wie C oder C? In dem Sinne, dass Sie eine Aufzählung mit Feldern definieren können, die automatisch an Wert zunehmen und bei einem gegebenen Wert beginnen?Java Enum Auto-Inkrement Einträge?

z.

In C oder C#:

enum Foo { A = 10, B, C, D = 5000, E, Fish }; 

Renditen A = 10, B = 11, C = 12, D = 5000, E = 5001, Fische = 5002.

Antwort

10

In Java können Sie‘ t Geben Sie die Ordinalwerte explizit überhaupt an. Sie autoincremente immer von 0, ohne Kontrolle darüber.

Wenn Sie andere benutzerdefinierte Werte wünschen, müssen Sie sie in Konstruktoraufrufe einfügen und sie selbst speichern. Sie können bekommen Autoinkrement, aber es ist eklig, wie Heck:

import java.util.EnumSet; 

// Please don't ever use this code. It's here so you can point and laugh. 
enum Foo 
{ 
    A(10), B, C, D(5000), E, Fish; 

    private static int nextValue; 
    private int value; 

    private Foo() 
    { 
     this(Counter.nextValue); 
    } 

    private Foo(int value) 
    { 
     this.value = value; 
     Counter.nextValue = value + 1; 
    } 

    public int getValue() 
    { 
     return value; 
    } 

    private static class Counter 
    { 
     private static int nextValue = 0; 
    } 
} 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     for (Foo foo : EnumSet.allOf(Foo.class)) 
     { 
      System.out.println(foo.name() + " " + 
           foo.ordinal() + " " + 
           foo.getValue()); 
     } 
    } 
} 

Beachten Sie die Notwendigkeit für die verschachtelte Klasse, weil Sie nicht statische Felder innerhalb eines ENUM-Konstruktor zugreifen können. Ick, ick, ick. Bitte tu das nicht.

+0

'Es ist hier, damit Sie zeigen können und laugh.' Haha. Gute! –

+0

Der Standardkonstruktor muss Counter.nextValue aktualisieren, und Sie können den nextValue wahrscheinlich auch direkt in Foo entfernen. – Yuliy

+0

@Yuliy: Der parameterlose Konstruktor ruft den parametrisierten Konstruktor auf, so dass Counter.nextValue aktualisiert wird. Den nextValue direkt in Foo * zu setzen funktioniert nicht - zumindest nicht mit der Version von javac, die ich benutze. Es verhindert, dass Sie auf statische Felder im Konstruktor zugreifen. –

10

Dies ist eine Entwurfsauswahl von Java Enums, die das Ändern der Ordinalwerte nicht unterstützt. Grundsätzlich sind sie nicht stabil genug, um von ihnen abhängig zu sein. Wenn Sie die Position von B und C in Ihrem Beispiel ändern, werden die Clients abhängig von den Ordinalwerten gebrochen. Dies kann unbeabsichtigt passieren.

Das Problem wird in Effektiv Java Item 31: Use instance field instead of ordinals beschrieben.

Sie können das Verhalten in einer stabilen Weise emulieren:

enum X{ 
    A(10), B(A), C(B), D(5000), E(D), F(E); 

    private final int value; 

    X(int value){ 
     this.value = value; 
    } 

    X(X preceding){ 
     this.value = preceding.getValue() + 1; 
    } 

    public int getValue() { 
     return value; 
    } 

    @Override 
    public String toString() { 
     return this.name() + "(" + this.value + ")"; 
    } 

    static { 
     Set<Integer> values = new HashSet<Integer>(); 
     for(X x : X.values()) values.add(x.value); 
     assert(values.size() == X.values().length); //no duplicates 
    } 
} 

Mit dieser Definition der Reihenfolge der Werte, die Sie können, ohne Kunden zu brechen ändern.

Aufruf for(X x : X.values()) System.out.println(x); Rückkehr:

A(10) 
B(11) 
C(12) 
D(5000) 
E(5001) 
F(5002) 
+0

Kinda saugt zwar - sie haben ihren Nutzen. Zum Beispiel, wenn ich nicht möchte, dass meine enums Werte unter N haben (vielleicht möchte ich diesen Bereich als tabu reservieren). Aber ich möchte auch nicht jeden Integer-Wert manuell definieren ... –

+0

@alex: das bedeutet, dass Sie ein Feld haben sollten, das die Zahl aus der Ordinalzahl berechnet, vorausgesetzt, dass Sie die Einschränkungen des gewünschten Bereichs haben. – Chii

+0

@Alex - Die emulierte Version sollte für Sie arbeiten. Es ist stabil und Sie müssen nicht jeden int-Wert explizit eingeben. –