如何在Java中避免重复的枚举值?

6
我有一个枚举与编码对应。我需要检查枚举中没有重复的编码数字值。
public enum EncodingsEnum
{

ISO8859_1("ISO-8859-1",0), ISO8859_2("ISO-8859-2",1),
    ISO8859_3("ISO-8859-3",2), ISO8859_4("ISO-8859-4",3),
    ISO8859_5("ISO-8859-5",4), ISO8859_6("ISO-8859-6",5),
    ISO8859_7("ISO-8859-7",6), ISO8859_8("ISO-8859-8",7),
    ISO8859_9("ISO-8859-9",8), ISO8859_11("ISO-8859-11",9),
    ISO8859_13("ISO-8859-13",10),ISO8859_15("ISO-8859-15",11),
    UTF_8("UTF-8",11);

    public static final int ENCODINGS_COUNT = EncodingsEnum.values().length;
    private final String encodingName;
    private final int encodingNumber;

    EncodingsEnum(final String encodingName,int encodingNumber)
    {
        ReferenceChecker.checkReferenceNotNull(encodingName);

        this.encodingName = encodingName;
        this.encodingNumber = encodingNumber;
    }

    public static String getEncodingNameByNumber(int number)
    {
        for(EncodingsEnum encoding : EncodingsEnum.values())
        {
            if(encoding.encodingNumber == number)
            {
                return encoding.getEncodingName();
            }
        }
        throw new RuntimeException("Encoding with this number isn't supported:" + number);

    }

    public static int getEncodingNumberByName(final String name)
    {
        for(EncodingsEnum encoding : EncodingsEnum.values())
        {
            if(encoding.encodingName.equals(name))
            {
                return encoding.getEncodingNumber();
            }
        }
        throw new RuntimeException("Encoding with this name isn't supported:" + name);
    }

    public String getEncodingName()
    {
        return this.encodingName;
    }

    public int getEncodingNumber()
    {
        return this.encodingNumber;
    }
}

我遇到了一个问题,就是我可以创建与现有编码相同的编码,因此我需要检查枚举是否包含具有此编号的元素,并抛出异常。但我不知道怎么做,请问有什么想法吗?谢谢。


“我可以创建与现有编码之一具有相同编号的编码”是什么意思?在编译时,EncodingsEnum.java的内容是final的 - 您无法在运行时创建新的枚举常量。在添加新常量之前,请尝试使用Ctrl-f查找。 - Gustav Barkefors
为什么不直接将序数作为数字呢?这样语言可以保证它是唯一的。 - Joachim Sauer
据我所知,使用序数是一种不好的做法。 - Avershin Dmitry
@AvershinDmitry:是的,但是用自己的近似序数以一种容易出错、手动的方式并不比现有方法好多少。 - Joachim Sauer
3个回答

10

不应该在运行时检查这个问题:太晚了。添加一个单元测试循环枚举值,并检查它们是否都具有不同的数字。确保在生成您的应用程序/库的新版本之前始终执行单元测试,并检查它们是否通过。

@Test
public void encodingNumbersMustBeUnique() {
    Set<Integer> numbers = new HashSet<Integer>();
    for (EncodingsEnum e : EncodingsEnum.values()) {
        assertFalse(numbers.contains(e.getEncodingNumber()));
        numbers.add(e.getEncodingNumber());
    }
}

“太晚了” - 这样做非常浪费时间。为什么一遍又一遍地检查呢? - duffymo

1

使用以下代码解决问题。

public enum EncodingsEnum {
     ONE(1),
     TWO(1);

     private int number;

     static {
      if (!test()) {
       throw new RuntimeException();
      }
     }

     private EncodingsEnum(int number) {
      this.number = number;
     }

     public static boolean test() {
      final Set<Integer> numbers = new HashSet<Integer>();

      for (final EncodingsEnum enc : EncodingsEnum.values()) {
       numbers.add(enc.number);
      }

      return numbers.size() == EncodingsEnum.values().length;
     }

这个方法是可行的,但并不总是在应用程序加载时执行。我使用了这种方式,但在单元测试中执行它会更好,就像目前被接受的答案所示:https://dev59.com/HGbWa4cB1Zd3GeqPSxL-#11467629 - David Balažic

0
在构造函数中,您可以遍历枚举的encodingNumbers,并在发现重复时抛出异常。类似这样的代码:
public boolean isPresent(String type){
            ClassType[] typeArray = ClassType.values();
            for(ClassType cType: typeArray){
                if(cType.absoluteName/*this is some private field of the class*/.equalsIgnoreCase(type)){
                    return true;
                }
            }
            return false;
        }

在你的构造函数中给数字和名称赋值之前,加上上述检查。如果检查返回true,则抛出异常,否则不抛出。

在构造函数中调用 ClassType.values(); 会抛出 NullPointerException 异常(在 Java 1.6 中;我正在处理一些旧项目,也会尝试新版本)。 - David Balažic
Java 11中相同。 - David Balažic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接