比较Java枚举成员:==或equals()?
我知道,Java枚举被编译为具有私有构造函数和一堆公共静态成员的类。 比较给定枚举的两个成员时,我总是使用.equals()
,例如
public useEnums(SomeEnum a) { if(a.equals(SomeEnum.SOME_ENUM_VALUE)) { ... } ... }
但是,我刚刚遇到一些使用等号运算符==
而不是.equals()的代码:
public useEnums2(SomeEnum a) { if(a == SomeEnum.SOME_ENUM_VALUE) { ... } ... }
我应该使用哪一个操作符?
两者在技术上是正确的。 如果您查看.equals()
的源代码,则只需按照==
。
我使用==
,但是,因为这将是无效的。
==
可以用于enum
?
是的:枚举具有严格的实例控件,允许您使用==
来比较实例。 这是语言规范提供的保证:
JLS 8.9枚举
一个枚举types除了由枚举常量定义的枚举types之外没有其他实例。
试图显式实例化一个枚举types是一个编译时错误。
Enum
的final clone
方法确保enum
常量永远不会被克隆,并且序列化机制的特殊处理可以确保重复实例从不会因为反序列化而被创build。 禁止枚举types的reflection实例化。 总之,这四件事情确保enum
types的实例不会超出enum
常量所定义的那些实例。因为每个
enum
只有一个实例是常量, 所以当比较两个对象引用时,如果知道至less有一个引用了enum
常量, 则允许使用==
运算符代替equals
方法 。 (Enum
的equals
方法是一个final
方法,它只是在其参数上调用super.equals
并返回结果,从而执行标识比较。
这个保证足够强大,Josh Blochbuild议,如果你坚持使用单例模式,实现它的最好方法是使用单元素enum
(请参阅: Effective Java第2版,第3项:强制单例属性一个私有构造函数或一个枚举types ;也是单线程中的线程安全 )
==
和equals
什么区别?
提醒一下,通常情况下, ==
不equals
。 但是,如果是(比如enum
),则需要考虑两个重要的区别:
==
从不抛出NullPointerException
enum Color { BLACK, WHITE }; Color nothing = null; if (nothing == Color.BLACK); // runs fine if (nothing.equals(Color.BLACK)); // throws NullPointerException
==
在编译时需要进行types兼容性检查
enum Color { BLACK, WHITE }; enum Chiral { LEFT, RIGHT }; if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine if (Color.BLACK == Chiral.LEFT); // DOESN'T COMPILE!!! Incompatible types!
应该在适用时使用==
Bloch特别提到,具有对其实例的适当控制的不可变类可以保证它们的客户端==
可用。 enum
是特别提到的例证。
项目1:考虑静态工厂方法,而不是构造函数
它允许一个不可变的类来保证不存在两个相等的实例:
a.equals(b)
当且仅当a==b
。 如果一个类做出这个保证,那么它的客户可以使用==
运算符而不是equals(Object)
方法,这可能会导致性能的提高。 枚举types提供了这个保证。
总而言之,在enum
上使用==
的参数是:
- 有用。
- 速度更快
- 运行时更安全。
- 编译时更安全。
使用==
来比较两个枚举值的作品,因为每个枚举常量只有一个对象。
在附注中,如果你写下你的equals()
,实际上不需要使用==
来写空安全代码:
public useEnums(SomeEnum a) { if(SomeEnum.SOME_ENUM_VALUE.equals(a)) { ... } ... }
这是一个最佳做法,称为比较左边的常数 ,你绝对应该遵循。
正如其他人所说,在大多数情况下, ==
和.equals()
工作。 编译时确定性,你没有比较完全不同types的对象,其他人已经指出是有效和有益的,但比较对象的两种不同的编译时间types的特定types的bug也可以findFindBugs(也可能Eclipse / IntelliJ编译时间检查),所以Java编译器发现它并不会增加额外的安全性。
然而:
-
==
从来没有抛出NPE的事实是==
的缺点 。 应该几乎不需要enum
types为null
,因为任何额外的状态,你可能要通过null
表示可以添加到enum
作为一个额外的实例。 如果它出乎意料地为null
,我宁愿有一个NPE比==
默默地评估为假。 所以我不同意在运行时意见比较安全 。 最好不要让enum
值为@Nullable
。 -
==
更快的论点也是假的。 在大多数情况下,您会在编译时types为enum类的variables上调用.equals()
,在这种情况下,编译器可以知道这与==
相同(因为enum
的equals()
方法可以不被覆盖),并可以优化function调用。 我不确定目前编译器是否执行了这个操作,但是如果不这样做,并且总体上是Java性能问题,那么我宁愿修复编译器,也不愿意让100,000个Java程序员改变他们的编程风格一个特定的编译器版本的性能特点。 -
enums
是对象。 对于所有其他对象types,标准比较是.equals()
,而不是==
。 我认为对enums
进行例外是很危险的,因为你可能会意外地把对象与==
而不是equals()
相比较,特别是当你将一个enum
重构为一个非枚举类时。 在这样的重构的情况下,从上面的工作点是错误的。 为了说服你自己==
的使用是正确的,你需要检查是否值是一个enum
或一个原语; 如果它是一个非enum
类,它会是错的,但容易错过,因为代码仍然会编译。 当使用.equals()
会出错时,唯一的情况是如果这些值是原始的, 在这种情况下,代码不会编译,所以很难错过。 因此,.equals()
更容易被识别为正确的,并且对未来的重构更安全。
我实际上认为Java语言应该在Object上定义==来调用左侧值的.equals(),并引入一个单独的运算符来标识对象,但这不是Java定义的方式。
总之,我仍然认为参数赞成使用enum
types的.equals()
。
这是一个粗略的时间testing比较两个:
import java.util.Date; public class EnumCompareSpeedTest { static enum TestEnum {ONE, TWO, THREE } public static void main(String [] args) { Date before = new Date(); int c = 0; for(int y=0;y<5;++y) { for(int x=0;x<Integer.MAX_VALUE;++x) { if(TestEnum.ONE.equals(TestEnum.TWO)) {++c;} if(TestEnum.ONE == TestEnum.TWO){++c;} } } System.out.println(new Date().getTime() - before.getTime()); } }
一次一个地评论IFs。 这里是从反汇编的字节码上面的两个比较:
21 getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19] 24 getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25] 27 invokevirtual EnumCompareSpeedTest$TestEnum.equals(java.lang.Object) : boolean [28] 30 ifeq 36 36 getstatic EnumCompareSpeedTest$TestEnum.ONE : EnumCompareSpeedTest.TestEnum [19] 39 getstatic EnumCompareSpeedTest$TestEnum.TWO : EnumCompareSpeedTest.TestEnum [25] 42 if_acmpne 48
第一个(等于)执行虚拟调用并testing堆栈中的返回布尔值。 第二个(==)直接从堆栈中比较对象地址。 在第一种情况下,有更多的活动。
我同时使用两个IF来执行这个testing多次。 “==”的速度要快一些。
我宁愿使用==
代替equals
:
其他的原因,除了这里已经讨论过的其他原因,你可以引入一个错误,而不意识到它。 假设你有这个枚举是完全一样的,但在分离的pacakges中(这不常见,但可能发生):
首先枚举 :
package first.pckg public enum Category { JAZZ, ROCK, POP, POP_ROCK }
第二枚枚举:
package second.pckg public enum Category { JAZZ, ROCK, POP, POP_ROCK }
然后假设你在item.category
使用item.category
中的下一个item.category
,但是你first.pckg.Category
导入第二个枚举( second.pckg.Category
)而不是第一个:
import second.pckg.Category; ... Category.JAZZ.equals(item.getCategory())
所以你会得到allways false
因为是一个不同的枚举,虽然你期望真实,因为item.getCategory()
是JAZZ
。 这可能有点难以看到。
所以,如果你使用运算符==
你将会有一个编译错误:
operator ==不能应用于“second.pckg.Category”,“first.pckg.Category”
import second.pckg.Category; ... Category.JAZZ == item.getCategory()
在枚举的情况下都是正确的,正确的!
使用除了==
之外的任何东西来比较枚举常量是无稽之谈。 这就像比较class
对象与equals
– 不要这样做!
但是,Sun JDK 6u10及更早版本中存在一个令人讨厌的错误(BugID 6277781 ),可能因历史原因而感兴趣。 这个错误阻止了在反序列化枚举上正确使用==
,虽然这可以说是一个angular落的情况。
枚举类是为public static final field
(immutable)声明的每个枚举常量返回一个实例(类似singletons)的类,以便可以使用==
运算符来检查它们的相等性,而不是使用equals()
方法
总之,两者都有优点和缺点。
一方面,如其他答案所述,使用==
具有优势。
另一方面,如果你出于任何原因用一种不同的方法(普通的类实例)replace枚举,使用==
咬你。 (BTDT)。
您可以使用: ==,equals()或switch()块来比较枚举,所有这些在技术上都是真实的,并且可以满足您的需求。
查看本教程以了解有关Enum的常见操作的更多信息: 如何在java中使用Enums
我想补充polygenelubricants答案:
我个人更喜欢equals()。 但它湖型的兼容性检查。 我认为这是一个重要的限制。
要在编译时进行types兼容性检查,请在枚举中声明并使用自定义函数。
public boolean isEquals(enumVariable) // compare constant from left public static boolean areEqual(enumVariable, enumVariable2) // compare two variable
有了这个,你就得到了两个解决scheme的所有优点:NPE保护,编译时易于读取代码和types兼容性检查。
我也build议为枚举添加一个UNDEFINED值。
我想明确地突出==
运算符和equals()
方法之间的这种特定区别:
equals()
方法是用来检查对象引用的引用variables是否相同的对象内容 。
==
运算符检查涉及的引用variables是否引用同一个对象 。
由应用程序根据需要提供差异化的实现类。
否则,默认行为将由Object
类(Java中)提供,如http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#equals(; java.lang.Object) :
Object
类的equals
方法实现Object
上最可能的等价关系; 也就是说,对于任何非null的引用值x
和y
,当且仅当x
和y
引用同一个对象(x == y
的值为true
)时,此方法返回true
。