我有一个枚举:
public enum Persons {
CHILD,
PARENT,
GRANDPARENT;
}
使用ordinal()
方法检查枚举成员之间的“层次结构”是否有任何问题?我的意思是 - 在使用它时除了冗长之外还有任何缺点,当有人可能在将来意外改变时。
或者做这样的事情会更好:
public enum Persons {
CHILD(0),
PARENT(1),
GRANDPARENT(2);
private Integer hierarchy;
private Persons(final Integer hierarchy) {
this.hierarchy = hierarchy;
}
public Integer getHierarchy() {
return hierarchy;
}
}
答案 0 :(得分:39)
如果您在ordinal
中引用Enum.java
方法的javadoc:
大多数程序员都没有使用此方法。它是 设计用于复杂的基于枚举的数据结构,例如 作为
java.util.EnumSet
和java.util.EnumMap
。
首先 - 阅读手册(本例中为javadoc)。
其次 - 不要写脆弱的代码。枚举值可能在将来发生变化,您的第二个代码示例更加清除和可维护。
如果在PARENT
和GRANDPARENT
之间插入新的枚举值,您绝对不想为将来制造问题。
答案 1 :(得分:10)
第一种方式不能直接理解,因为您必须阅读使用枚举的代码,以了解枚举的顺序是否重要。
这很容易出错。
public enum Persons {
CHILD,
PARENT,
GRANDPARENT;
}
第二种方式更好,因为自我解释:
CHILD(0),
PARENT(1),
GRANDPARENT(2);
private SourceType(final Integer hierarchy) {
this.hierarchy = hierarchy;
}
当然,枚举值的顺序应该与枚举构造函数参数提供的层次顺序一致。
引入了一种冗余,因为枚举值和枚举构造函数的参数都传达了它们的层次结构。
但为什么会出现问题?
枚举旨在表示不变且不经常更改的值
OP枚举用法说明了良好的枚举用法:
CHILD, PARENT, GRANDPARENT
枚举不是为了表示频繁移动的值
在这种情况下,使用枚举可能不是最佳选择,因为它可能经常破坏使用它的客户端代码,并且除了它强制在每次修改枚举值时重新编译,重新打包和重新部署应用程序。
答案 2 :(得分:7)
正如Joshua Bloch在 Effective Java 中所建议的那样,从序数中获取与枚举相关联的值并不是一个好主意,因为对枚举值的排序的更改可能会打破你编码的逻辑。
您提到的第二种方法完全遵循作者提出的建议,即将值存储在单独的字段中。
我想说你建议的替代方案肯定更好,因为它更加可扩展和可维护,因为你正在解耦枚举值的排序和层次结构的概念。
答案 3 :(得分:6)
首先,您可能甚至不需要数字订单值 - 那就是
什么Comparable
用于,Enum<E>
实现Comparable<E>
。
如果 出于某种原因需要数字订单值,是的,您应该这样做
使用ordinal()
。这就是它的用途。
Java Enums
的标准做法是按声明顺序排序,
这就是Enum<E>
实现Comparable<E>
的原因以及原因
Enum.compareTo()
为final
。
如果添加自己的非标准比较代码,则不使用
Comparable
并且不依赖于声明顺序,你只是
会混淆任何试图使用您的代码的人,包括
你自己未来的自我。没有人会期望代码存在;
他们希望Enum
为Enum
。
如果自定义订单与声明订单不匹配,则任何人 看着宣言会很困惑。如果它 (碰巧,此时此刻)与声明令相匹配,任何人 看着它会有所期待,而且他们会去 如果在未来的某个日期它没有,那就会受到惊吓。 (如果你写 代码(或测试)确保自定义订单匹配 声明顺序,你只是强化它是多么不必要。)
如果您添加自己的订单值,则会产生维护问题 为你自己:
hierarchy
值是唯一的如果您担心有人可能会意外更改订单 未来,写一个检查订单的单元测试。
总而言之,在Item 47的不朽话语中: 知道并使用库。
P.S。另外,如果您的意思是Integer
,请不要使用int
。
答案 4 :(得分:5)
由于枚举声明中的更改可能会影响序数值,因此建议不要使用ordinal()
。
<强>更新强>
值得注意的是,枚举字段是常量,可以有重复的值,即
enum Family {
OFFSPRING(0),
PARENT(1),
GRANDPARENT(2),
SIBLING(3),
COUSING(4),
UNCLE(4),
AUNT(4);
private final int hierarchy;
private Family(int hierarchy) {
this.hierarchy = hierarchy;
}
public int getHierarchy() {
return hierarchy;
}
}
根据您计划对hierarchy
执行的操作,这可能会造成损害或有益。
此外,您可以使用枚举常量来构建您自己的EnumFlags
,而不是使用EnumSet
,例如
答案 5 :(得分:5)
如果您只想在枚举值之间创建关系,您实际上可以使用其他枚举值的技巧:
public enum Person {
GRANDPARENT(null),
PARENT(GRANDPARENT),
CHILD(PARENT);
private final Person parent;
private Person(Person parent) {
this.parent = parent;
}
public final Parent getParent() {
return parent;
}
}
请注意,您只能使用在您尝试声明之前以词汇方式声明的枚举值,因此这仅适用于您的关系形成非循环有向图(并且您声明它们的顺序是有效拓扑的情况)排序)。
答案 6 :(得分:3)
我会使用你的第二个选项(使用显式整数),因此数值是由你而不是由Java分配的。
答案 7 :(得分:1)
根据java doc
返回此枚举常量的序数(它在其中的位置) 枚举声明,其中初始常量被赋值为序数 零)。大多数程序员都没有使用这种方法。它是 设计用于复杂的基于枚举的数据结构,例如 EnumSet和EnumMap。
您可以通过更改枚举的顺序来控制序号,但不能明确地设置它。一种解决方法是在枚举中为您想要的数字提供额外的方法。
enum Mobile {
Samsung(400), Nokia(250),Motorola(325);
private final int val;
private Mobile (int v) { val = v; }
public int getVal() { return val; }
}
在这种情况下Samsung.ordinal() = 0
,但Samsung.getVal() = 400
。
答案 8 :(得分:1)
这不是您问题的直接答案。更好的方法用于您的用例。这样可以确保下一个开发人员明确知道不应更改分配给属性的值。
创建一个具有静态属性的类,它将模拟你的枚举:
public class Persons {
final public static int CHILD = 0;
final public static int PARENT = 1;
final public static int GRANDPARENT = 2;
}
然后像enum一样使用:
Persons.CHILD
它适用于大多数简单的用例。否则,您可能会错过valueOf(),EnumSet,EnumMap或values()等选项。
答案 9 :(得分:0)
让我们考虑以下示例:
我们需要在Spring应用程序中订购几个过滤器。这可以通过FilterRegistrationBeans注册过滤器来实现:
@Bean
public FilterRegistrationBean compressingFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(compressingFilter());
registration.setName("CompressingFilter");
...
registration.setOrder(1);
return registration;
}
假设我们有几个过滤器,我们需要指定它们的顺序(例如,我们要首先设置为所有记录器添加MDC上下文JSID的过滤器)
在这里,我看到了ordinal()
的完美用例。让我们创建一个枚举:
enum FilterRegistrationOrder {
MDC_FILTER,
COMPRESSING_FILTER,
CACHE_CONTROL_FILTER,
SPRING_SECURITY_FILTER,
...
}
现在在注册bean中,我们可以使用:
registration.setOrder(MDC_FILTER.ordinal());
在我们的情况下,它非常有效。如果没有枚举,则必须对所有过滤器订单加1(或向存储它们的常数添加)以重新计算其数量。当我们有枚举时,您只需要在枚举的适当位置添加一行并使用序数即可。我们不必在很多地方更改代码,而且我们所有过滤器的顺序结构都清晰易懂。
在这种情况下,我认为ordinal()
方法是以干净且可维护的方式实现过滤器顺序的最佳选择