开门见山地说吧,enum(枚举)是 Java 1.5 时引入的关键字,它表示一种特殊类型的类,默认继承自 java.lang.Enum。
为了证明这一点,我们来新建一个枚举 PlayerType:
public enum PlayerType { TENNIS, FOOTBALL, BASKETBALL }
两个关键字带一个类名,还有大括号,以及三个大写的单词,但没看到继承 Enum 类啊?别着急,心急吃不了热豆腐啊。使用 JAD 查看一下反编译后的字节码,就一清二楚了。
public final class PlayerType extends Enum { public static PlayerType[] values() { return (PlayerType[])$VALUES.clone(); } public static PlayerType valueOf(String name) { return (PlayerType)Enum.valueOf(com/cmower/baeldung/enum1/PlayerType, name); } private PlayerType(String s, int i) { super(s, i); } public static final PlayerType TENNIS; public static final PlayerType FOOTBALL; public static final PlayerType BASKETBALL; private static final PlayerType $VALUES[]; static { TENNIS = new PlayerType("TENNIS", 0); FOOTBALL = new PlayerType("FOOTBALL", 1); BASKETBALL = new PlayerType("BASKETBALL", 2); $VALUES = (new PlayerType[] { TENNIS, FOOTBALL, BASKETBALL }); } }
看到没?PlayerType 类是 final 的,并且继承自 Enum 类。这些工作我们程序员没做,编译器帮我们悄悄地做了。此外,它还附带几个有用静态方法,比如说 values() 和 valueOf(String name)。
01、内部枚举
好的,小伙伴们应该已经清楚枚举长什么样子了吧?既然枚举是一种特殊的类,那它其实是可以定义在一个类的内部的,这样它的作用域就可以限定于这个外部类中使用。
public class Player { private PlayerType type; public enum PlayerType { TENNIS, FOOTBALL, BASKETBALL } public boolean isBasketballPlayer() { return getType() == PlayerType.BASKETBALL; } public PlayerType getType() { return type; } public void setType(PlayerType type) { this.type = type; } }
PlayerType 就相当于 Player 的内部类,isBasketballPlayer() 方法用来判断运动员是否是一个篮球运动员。
由于枚举是 final 的,可以确保在 Java 虚拟机中仅有一个常量对象(可以参照反编译后的静态代码块「static 关键字带大括号的那部分代码」),所以我们可以很安全地使用“==”运算符来比较两个枚举是否相等,参照 isBasketballPlayer() 方法。
那为什么不使用 equals() 方法判断呢?
if(player.getType().equals(Player.PlayerType.BASKETBALL)){};
if(player.getType() == Player.PlayerType.BASKETBALL){};
1
2
“==”运算符比较的时候,如果两个对象都为 null,并不会发生 NullPointerException,而 equals() 方法则会。
另外, “==”运算符会在编译时进行检查,如果两侧的类型不匹配,会提示错误,而 equals() 方法则不会。
02、枚举可用于 switch 语句
这个我在之前的一篇我去的文章中详细地说明过了,感兴趣的小伙伴可以点击链接跳转过去看一下。
switch (playerType) { case TENNIS: return "网球运动员费德勒"; case FOOTBALL: return "足球运动员C罗"; case BASKETBALL: return "篮球运动员詹姆斯"; case UNKNOWN: throw new IllegalArgumentException("未知"); default: throw new IllegalArgumentException( "运动员类型: " + playerType); }
03、枚举可以有构造方法
如果枚举中需要包含更多信息的话,可以为其添加一些字段,比如下面示例中的 name,此时需要为枚举添加一个带参的构造方法,这样就可以在定义枚举时添加对应的名称了。
public enum PlayerType { TENNIS("网球"), FOOTBALL("足球"), BASKETBALL("篮球"); private String name; PlayerType(String name) { this.name = name; } }
04、EnumSet
EnumSet 是一个专门针对枚举类型的 Set 接口的实现类,它是处理枚举类型数据的一把利器,非常高效(内部实现是位向量,我也搞不懂)。
因为 EnumSet 是一个抽象类,所以创建 EnumSet 时不能使用 new 关键字。不过,EnumSet 提供了很多有用的静态工厂方法:
下面的示例中使用 noneOf() 创建了一个空的 PlayerType 的 EnumSet;使用 allOf() 创建了一个包含所有 PlayerType 的 EnumSet。
public class EnumSetTest { public enum PlayerType { TENNIS, FOOTBALL, BASKETBALL } public static void main(String[] args) { EnumSet<PlayerType> enumSetNone = EnumSet.noneOf(PlayerType.class); System.out.println(enumSetNone); EnumSet<PlayerType> enumSetAll = EnumSet.allOf(PlayerType.class); System.out.println(enumSetAll); } }
程序输出结果如下所示:
[]
[TENNIS, FOOTBALL, BASKETBALL]
有了 EnumSet 后,就可以使用 Set 的一些方法了: