枚举的特性

枚举使用关键字 enum 进行定义,每个元素都是一个实例,如下,FOO 和 BAR 都是一个 EnumClazz 实例。

public enum EnumClazz {
FOO,
BAR;
}


枚举类默认继承 Enum 类。

方法添加

我们可以给枚举实例添加一些对自身的描述,这是通过构造函数实现的

public enum EnumClazz {
FOO("this is foo"),
BAR("this is bar");

EnumClazz(String desc) {
}
}


但是这样是拿不到 desc 信息的,需要为其添加一个获取 desc 的方法。

public enum EnumClazz {
FOO("this is foo"),
BAR("this is bar");

private String desc;

private String getDesc(){
return desc;
}

EnumClazz(String desc) {
this.desc = desc;
}
}


values 方法

我们定义的枚举类都是继承自 Enum 类,但是 Enum 类并没有 values() 方法,那么 values() 方法是怎么来的呢,答案是编译器自动帮我们生成的。values() 方法可以获取到枚举类的所有实例,下面是常规使用方法:

value() 方法是接收一个 code 值,获取对应的枚举实例。

public enum EnumClazz {
FOO(1),
BAR(2);

private Integer code;

private Integer getCode() {
return code;
}

EnumClazz(Integer code) {
this.code = code;
}

public EnumClazz value(Integer code) {
if (code == null) return null;
for (EnumClazz value : EnumClazz.values()) {
if (Objects.equals(value.getCode(), code)) {
return value;
}
}
return null;
}
}


EnumSet

枚举类的每个实例其实都维护着一个顺序值,按照定义顺序来递增

EnumSet 采用 bit 位来实现,内部是使用 Long 类型来维护的,哪个枚举被塞入 EnumSet ,那么对应的 bit 位就制为1,由于是采用 bit 位进行操作的,因此速度非常快。Long 只有 64 位,但是 EnumSet 却可以存储超过 64 个枚举实例,估摸着是采用了多个 Long。

public enum EnumClazz {
FOO,
BAR;
public static void main(String[] args) {
System.out.println(EnumClazz.FOO.ordinal());
System.out.println(EnumClazz.BAR.ordinal());
EnumSet<EnumClazz> set = EnumSet.allOf(EnumClazz.class);
System.out.println(set.contains(EnumClazz.FOO));
}
}


输出:

0

1

true

EnumMap

EnumMap 的 key 只能是枚举,由于枚举的特殊性,每个实例都有自己的一个顺序值,可以用来定位,因此,直接采用数值来实现即可,我们知道,在知道数组下标的情况下,数组的顺序是非常快的。

public enum EnumClazz {
FOO,
BAR;
public static void main(String[] args) {
EnumMap<EnumClazz, String> map = new EnumMap<EnumClazz, String>(EnumClazz.class);
map.put(EnumClazz.FOO, "foo");
map.put(EnumClazz.BAR, "bar");
System.out.println(map);
}
}


输出:

{FOO=foo, BAR=bar}

常量特定方法

由于每个属性都是一个枚举实例,那么,我们是不是可以在枚举类中定义方法,然后在实例中实现他们,从而采取不同的行为呢,就像多态那样。这是可以做到的(描述的有点乱,直接看代码,代码一看就懂)。

如下,在枚举类 EnumClazz 中定义一个抽象方法,在每个实例,在这里是 FOO 实例和 BAR 实例中实现改方法即可。当然,不是抽象方法也行。

public enum EnumClazz {
FOO {
@Override
void showInfo() {
System.out.println("I am foo");
}
},
BAR {
@Override
void showInfo() {
System.out.println("I am bar");
}
};

abstract void showInfo();

public static void main(String[] args) {
EnumClazz.BAR.showInfo();
EnumClazz.FOO.showInfo();
}
}


输出:

I am bar

I am foo

有意思的枚举使用方式

在 Java 编程思想中,看到一个有意思的枚举使用方式,特此记录下来。

有时候,我们多个枚举类,但是这些枚举类又属于同一个大类目。举个栗子,水果下面分为香蕉、苹果、西瓜,香蕉分为米蕉、芝麻蕉、李林蕉,苹果分为:红玉苹果、金冠苹果、国光苹果,西瓜分为麒麟西瓜、黑美人西瓜、特小凤西瓜。其中可以创建水果枚举,香蕉枚举,苹果枚举,西瓜枚举,其中香蕉、苹果、西瓜枚举分别管理各自的品种,而水果枚举则管理着香蕉、苹果、西瓜枚举。也就是枚举的枚举。

这样的管理方式可以使用接口组织枚举

接口组织枚举

如下栗子,感觉还挺有意思的。

public enum FruitEnum {
BANANA(FruitInterface.Banana.class),
APPLE(FruitInterface.Apple.class),
WATERMELON(FruitInterface.Watermelon.class);

FruitInterface[] fruits;

FruitEnum(Class<? extends FruitInterface> clazz) {
fruits = clazz.getEnumConstants();
}

interface FruitInterface {
enum Banana implements FruitInterface {
MI,
LILIN,
SESAME;
}

enum Apple implements FruitInterface {
CARBUNCLE,
GOLDEN_ROWN,
GUO_GUANG;
}

enum Watermelon implements FruitInterface {
QILIN,
BLACK_BEAUTY,
TE_XIAO_FENG
}
}

public static void main(String[] args) {
Random ran = new Random();
FruitEnum[] fruits = FruitEnum.class.getEnumConstants();
FruitEnum fruit = fruits[ran.nextInt(3)];
FruitInterface[] randomFruits = fruit.fruits;
for (FruitInterface item : randomFruits) {
System.out.println(item);
}
}
}


输出

QILIN

BLACK_BEAUTY

TE_XIAO_FENG