【问题标题】:Java - enum with static counterJava - 带静态计数器的枚举
【发布时间】:2017-01-02 21:12:37
【问题描述】:

我想在 Java 中实现类似 C++ 的枚举,所以我想使用静态字段作为计数器。这是我目前的解决方案:

public enum ETest
{
    Test1(2),
    Test2,
    Test3,
    Test4(7),
    Test5;

    private static class CounterHolder
    {
        private static int mCount = 0;
    }

    ETest()
    {
        mValue = CounterHolder.mCount;
        CounterHolder.mCount++;
    }

    ETest(int pValue)
    {
        CounterHolder.mCount = pValue;
        mValue = CounterHolder.mCount;
        CounterHolder.mCount++;
    }

    public int toInt()
    {
        return mValue;
    }

    private final int mValue;
}

从上面的代码我应该得到: Test1.toInt() -> 2, Test2.toInt() -> 3, Test3.toInt() -> 4, Test4.toInt() -> 7, Test5.toInt() -> 8

此代码适用于经过测试的 Android Java VM,但我不确定它是否是有效的 Java 代码以及它是否能在所有有效 Java 的 VM 上正常工作,因为 AFAIK 枚举是在静态字段之前初始化的,但它看起来像在这种情况下,具有静态字段的嵌套类的按需初始化工作正常(没有嵌套类,简单的静态字段不起作用)。

最好的问候, 帕特里克

【问题讨论】:

  • 不要尝试将 C++ 结构转换为 Java。 Java 枚举的强大方法被滥用为简单的 C++ 样式值容器。
  • 我很确定该代码不会编译 - 请在问题中使用真实代码...
  • @GyroGearless 好吧,这段代码可以编译,因为我使用了按需初始化,我只是不确定它是否不会在其他JVM上导致运行时出现问题。
  • @TimothyTruckle 但是我只需要这个强大的枚举作为 C++ 中的简单值容器工作,仅此而已,所以如果这种类型真的像你说的那样强大,我应该能够实现这一点,但是我们会看到的。
  • @PatrykNadrowski “但我只需要这个强大的枚举作为来自 C++ 的简单值容器” 我认为你的意思是:我想坚持 C++编码人员认为 OOP 是... ;o)

标签: java android enums jvm


【解决方案1】:

CounterHolder 将始终在 ETest 初始化之前加载和初始化,因此您的代码应该在所有兼容的 JVM 上按预期工作。您可以在JLS 中阅读有关类初始化的更多信息(不确定 android 的等效文档是什么,但我希望它们的措辞非常相似)。特别是:

类或接口类型T 将在以下任何一项第一次出现之前立即初始化:

  • [...]
  • 使用了T 声明的静态字段,该字段不是常量变量。

所以最迟,CounterHolder 类将在ETest 的第一个构造函数运行之前被加载和初始化。

但是请注意,从风格的角度来看,只写可能会更干净:

enum ETest {
    Test1(2),
    Test2(3),
    Test3(4),
    Test4(7),
    Test5(8);
}

【讨论】:

    【解决方案2】:

    实际上,您可以像这样在枚举中使用Map 轻松做到这一点

    public enum TestEnum {
        Test1(2),
        Test2,
        Test3,
        Test4(7),
        Test5;
    
        private static Map<TestEnum, Integer> map = new HashMap<>();
    
        static {
            int counter = 0;
            for (TestEnum enun : TestEnum.values()) {
    
                counter = Math.max(counter, enun.value);
                if (enun.value == 0) {
                    counter++;
                }
                map.put(enun, counter);
            }
        }
    
        private int value;
    
        TestEnum() {
            value = 0;
        }
    
        TestEnum(int pValue) {
            value = pValue;
        }
    
        public int toInt() {
            return map.get(this);
        }
    }
    

    这是打印结果的函数

    for (TestEnum anEnum : TestEnum.values()) {
        System.out.println(anEnum.name() +" = "+ String.valueOf(anEnum.toInt()));
    }
    

    输出是:

    Test1 = 2
    Test2 = 3
    Test3 = 4
    Test4 = 7
    Test5 = 8
    

    提示:检查名为 ordinal 的东西,它是枚举常量的序数(它在枚举声明中的位置,初始常量的序数为零)。

    【讨论】:

    • 谢谢,但是我不希望这种简单任务产生与 HashMap 相关的开销。
    • 嗯,有EnumMap,正是为了避免“与HashMap相关的开销”,当键类型是enum类型时。当然,由于value 不是final,静态初始化器可以只覆盖零值而不是填充映射,并且toInt 可以像以前一样工作......
    【解决方案3】:

    在 android 中,最好的做法是避免使用 Enum,主要原因是反优化性能,基于 android 文档:

    枚举通常需要两倍于静态的内存 常数。您应该严格避免在 Android 上使用枚举。

    在其他方面 android 的目的是解决 type def 注释:

    @IntDef 注释让您基本上可以创建一个“typedef”,其中 您创建另一个表示有效整数的注释 你期望的常量,然后你用这个来装饰你的 API typedef 注释。

    这是一个示例,如何使用它(天数检查器...): https://noobcoderblog.wordpress.com/2015/04/12/java-enum-and-android-intdefstringdef-annotation/

    关于更多了解的功能,您可以阅读官方文档: https://developer.android.com/studio/write/annotations.html#enum-annotations

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-01
      • 1970-01-01
      • 2011-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-14
      • 1970-01-01
      相关资源
      最近更新 更多