2010-01-13 2 views
7

Является ли Java чем-то вроде хорошего ol 'C или даже C# в том смысле, что вы можете определить перечисление с полями, которые растут в значении автоматически и начинаются с необязательно заданного значения?Записи автоинкремента Java enum?

E.g.

В C или C#:

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

возвращающее = 10, B = 11, C = 12, D = 5000, E = 5001, Рыба = 5002.

ответ

10

В Java вы можете» t указывать порядковые значения явно вообще. Они всегда автоинкремента, от 0, без контроля над ним.

Если вы хотите другие пользовательские значения, вам нужно поместить их в вызовы конструктора и сохранить их самостоятельно. Вы можете получить автоинкрементные, но это неприглядное, как черт:

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()); 
     } 
    } 
} 

Обратите внимание на необходимость вложенного класса, потому что вы не можете получить доступ к статическим полям в конструкторе перечислений. Ick, ick, ick. Пожалуйста, не делайте этого.

+0

'Это здесь, так что вы можете указать и laugh.' Ха-ха. Неплохо! –

+0

конструктору по умолчанию необходимо обновить Counter.nextValue, и вы можете, вероятно, удалить nextValue непосредственно внутри Foo. – Yuliy

+0

@Yuliy: Безпараметрический конструктор вызывает параметризованный, поэтому он обновляет Counter.nextValue. Помещение nextValue непосредственно внутри Foo * не работает *, по крайней мере, не с версией javac, которую я использую. Это предотвращает доступ к статическим полям в конструкторе. –

10

Это дизайнерский выбор Java Enums, который не поддерживает изменение порядковых значений. В основном, они нестабильно достаточно, чтобы зависеть от них. Если вы измените положение B и C в вашем примере, клиенты будут в порядке, зависящие от порядковых значений. Это может произойти непреднамеренно.

Проблема описана в «Эффективной Java» Item 31: Use instance field instead of ordinals.

Вы можете эмулировать поведение стабильным образом:

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 
    } 
} 

С этим определением можно изменить порядок значений, не нарушая клиентов.

Вызывающие for(X x : X.values()) System.out.println(x); возвращается:

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

Kinda сосет хотя - они используют их. Например, когда я не хочу, чтобы мои перечисления имели значения ниже N, например (возможно, я хочу, чтобы этот диапазон был отключен). Но я также не хочу вручную определять каждое целочисленное значение самостоятельно ... –

+0

@alex: это означает, что у вас должно быть поле, которое вычисляет число из порядкового номера, учитывая ограничения диапазона, который вы хотите. – Chii

+0

@Alex - Эмутированная версия должна работать на вас. Он стабилен, и вам не нужно указывать каждую ценность int в явном виде. –