【问题标题】:How to define properties for Enum items如何定义枚举项的属性
【发布时间】:2010-06-16 14:33:32
【问题描述】:

我已经阅读了Difference of Enum between java and C++? 的问题,但我仍然感到困惑。

我希望以下返回相关的字符串:

public enum Checker {
    EMPTY ("Empty"),
    RED ("Red"),
    YELLOW ("Yellow");
}

根据我的阅读,这应该是可能的。只是希望您对如何实现它有所了解。

【问题讨论】:

    标签: java enums


    【解决方案1】:

    简答

    您需要一个构造函数、一个字段和一个 getter。

    构造函数

    枚举类型可以有构造函数,前提是它们的访问级别是私有的或默认的(包私有)。您不能直接调用这些构造函数,除非在枚举声明本身中。与类类似,当你定义一个不带参数的枚举常量时,实际上是调用了编译器生成的默认构造函数。例如

    public enum King {
        ELVIS
    }
    

    等价于

    public enum King {
        ELVIS() // the compiler will happily accept this
    }
    

    就像在类中一样,如果你定义了一个显式的构造函数,编译器不会插入一个默认的构造函数,所以这不会编译:

    public enum King {
        ELVIS, // error, default constructor is not defined
        MICHAEL_JACKSON(true)
        ;
        private boolean kingOfPop;
        King(boolean kingOfPop){this.kingOfPop = kingOfPop;}
    }
    

    这是a pretty good reference on enums,它也解释了构造函数问题。

    字段和访问器

    枚举是常量,因此是不可变的。但是,它们可以定义可以具有状态的字段。这是一种糟糕的做法,因为开发人员会期望枚举及其关联值是常量,但您仍然可以在具有 getter 和 setter 的枚举中定义非最终字段。

    这是合法的java代码:

    public enum Color {
        RED("FF0000"),
        GREEN("00FF00"),
        BLUE("0000FF");
        private String code;
        public String getCode(){return code;}
        public void setCode(String code){this.code = code;}
        private Color(String code){this.code = code;}
    }
    

    但它会启用这样的恶意代码:

    String oldBlue = Color.BLUE.getCode();
    Color.BLUE.setCode(Color.RED.getCode());
    Color.RED.setCode(oldBlue);
    

    因此,在 99.99% 的情况下:如果您的枚举中有字段,您应该将它们设为 final 并仅提供 getter。如果字段本身不是不可变的,请提供防御性副本:

    public enum Band {
        THE_BEATLES("John","Paul","George","Ringo");
        private final List<String> members;
        public List<String> getMembers(){
            // defensive copy, because the original list is mutable
            return new ArrayList<String>(members);
        }
        private Band(String... members){
            this.members=Arrays.asList(members);
        }
    }
    

    解决方案

    在您的情况下,它非常简单:您只需要一个字符串类型的字段(不可变),因此在构造函数中对其进行初始化并提供一个 getter 是完全可以的:

    public enum Checker {
    
        EMPTY ("Empty"),
        RED ("Red"),
        YELLOW ("Yellow");
    
        private final String value;
    
        private Checker(final String value) {
            this.value = value;
        }
    
        public String getValue() { return value; }
    }
    

    【讨论】:

    • @Joachim:没有二传手,我看不出有什么不同。
    • @Joachim:确切地说,我在您添加评论时添加了 :-)
    • @mmyers 我认为在最终字段中有一些编译器优化魔法
    • @mmyers 奇怪但真实。它在 JLS 中。允许运行时进行优化,否则它不会仅仅因为final 而进行优化。但添加final 的更好理由是有人可能想阅读代码。
    • @mmyers:除了汤姆说的,这也是记录意图的重要方式。没有理由应该改变字段,一旦构造函数完成并且不应该设置它,它应该总是被初始化。为什么不用一个准确表达这一点的关键字来记录这一点。
    【解决方案2】:

    如果模式成立,这也有效并消除了重复:

    public enum Checker {
        EMPTY,
        RED,
        YELLOW;
    
    
        public String getDescription(){
            String name = name();
            return ""+Character.toUpperCase(name.charAt(0))
                     +name.substring(1).toLowerCase();
        }
    
    }
    

    【讨论】:

    • 为什么这样更好?您每次都在重新分配所有这些字符串,我不会特别说这更具可读性。
    • 它避免了名称的重复(DRY 原则),并且不需要构造函数或字段(因此内存更少)。如果重新分配是一个问题并且使用内存不是问题,则可以将它们缓存在一个字段或一个 EnumMap 中。很可能这两种方式都没有关系,较短的解决方案更好。
    • 实际上,可能有两个构造函数,一个具有直接存储值的值,另一个是使用您的 getDescription 方法生成值的空构造函数。不过,我还是会把它们写到一个字段中
    • 显然你可以把 DRY 做得太过分了!甚至不确定枚举名称和“相关”String 是否总是以它们天真出现的方式相关。你可能想要FUSCHIA("Fuchsia"), WTF("Dusty Lavendar")
    • @Tom 没错,这就是我提出两个构造函数方案的原因
    猜你喜欢
    • 2011-07-03
    • 1970-01-01
    • 2017-04-02
    • 1970-01-01
    • 1970-01-01
    • 2020-07-19
    • 2016-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多