理解枚举类型

Posted 临_择

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解枚举类型相关的知识,希望对你有一定的参考价值。

枚举的定义

public enum AccountActionTypeEnum {
LOGIN,
REGISTER,
EDIT\\_INFO;

private AccountActionTypeEnum() {
}
}

值一般是大写的字母,多个值之间以逗号分隔。

枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进。下面看看如何在其他类中使用枚举类型

public static void main(String[] args){
//直接引用
AccountActionTypeEnum type =AccountActionTypeEnum.LOGIN;
}

枚举实现原理

我们大概了解了枚举类型的定义与简单使用后,现在来了解一下枚举类型的基本实现原理。实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类,也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类。下面我们编译前面定义的AccountActionTypeEnum.java并查看生成的class文件来验证这个结论:

javac AccountActionTypeEnum.java   #编译java文件生成AccountActionTypeEnum.class文件

然后通过jad来反编译AccountActionTypeEnum.class

.\\jad -sjava .\\AccountActionTypeEnum.class  #我这里的jad.exe放在AccountActionTypeEnum的同级目录,如果有加入环境变量的话,可以直接jad

反编译AccountActionTypeEnum.class的结果

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name:   AccountActionTypeEnum.java

<br>


public final class AccountActionTypeEnum extends Enum
{
// 编译器重写的方法
public static AccountActionTypeEnum[] values()
{
return (AccountActionTypeEnum[])$VALUES.clone();
}

// 编译器重写的方法
public static AccountActionTypeEnum valueOf(String s)
{
return (AccountActionTypeEnum)Enum.valueOf(AccountActionTypeEnum, s);
}

// 私有
private AccountActionTypeEnum(String s, int i)
{ // 调用enum的构造方法
super(s, i);
}

// 定义的四种枚举
// 定义类变量,这也就是为什么我们可以直接以AccountActionTypeEnum.LOGIN的形式使用的原因了
public static final AccountActionTypeEnum LOGIN;
public static final AccountActionTypeEnum REGISTER;
public static final AccountActionTypeEnum EDIT\\_INFO;
private static final AccountActionTypeEnum $VALUES[];

//静态代码块复制初始化类变量,在初始化阶段执行,实例化枚举实例
static
{
LOGIN = new AccountActionTypeEnum("LOGIN", 0);
REGISTER = new AccountActionTypeEnum("REGISTER", 1);
EDIT\\_INFO = new AccountActionTypeEnum("EDIT\\_INFO", 2);
$VALUES = (new AccountActionTypeEnum[] {
LOGIN, REGISTER, EDIT\\_INFO
});
}
}

从反编译的代码可以看出编译器确实帮助我们生成了一个AccountActionTypeEnum类(注意该类是final类型的,将无法被继承)而且该类继承自java.lang.Enum类,该类是一个抽象类,除此之外,编译器还帮助我们生成了4个AccountActionTypeEnum类型的实例对象分别对应枚举中定义的三种type和一个type数组。注意编译器还为我们生成了两个静态方法,分别是values()和 valueOf(),到此我们也就明白了,使用关键字enum定义的枚举类型,在编译期后,也将转换成为一个实实在在的类,而在该类中,会存在每个在枚举类型中定义好变量的对应实例对象,如上述的LOGIN枚举类型对应public static final AccountActionTypeEnum LOGIN;,同时编译器会为该类创建两个方法,分别是values()和valueOf()。下面我们深入了解一下java.lang.Enum类以及values()和valueOf()的用途。

Enum抽象类常见方法

Enum是所有 Java 语言枚举类型的公共基本类(注意Enum是抽象类),以下是它的常见方法:

返回类型方法名称方法说明
intcompareTo(E o)比较此枚举与指定对象的顺序
booleanequals(Object other)当指定对象等于此枚举常量时,返回 true。
Class<?>getDeclaringClass()返回与此枚举常量的枚举类型相对应的 Class 对象
Stringname()返回此枚举常量的名称,在其枚举声明中对其进行声明
intordinal()返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)
StringtoString()返回枚举常量的名称,它包含在声明中
static<T extends Enum<T>> Tstatic valueOf(Class<T> enumType, String name)返回带指定名称的指定枚举类型的枚举常量。

这里主要说明一下ordinal()方法,该方法获取的是枚举变量在枚举类中声明的顺序,下标从0开始,如AccountActionTypeEnum中的LOGIN在第一个位置,那么LOGIN的ordinal值就是0,如果LOGIN的声明位置发生变化,那么ordinal方法获取到的值也随之变化,注意在大多数情况下我们都不应该首先使用该方法,毕竟它总是变幻莫测的。如果我们把LOGIN放在第二个位置,那么它的ordinal方法的值也会相应的改变,下面我们将LOGIN改变位置编译,然后重新反编译AccountActionTypeEnum.class文件。

新的AccountActionTypeEnum.class的静态代码块

static
{
REGISTER = new AccountActionTypeEnum("REGISTER", 0);
// 可以看到当我们把LOGIN放在第二个位置,那么它的ordinal的值也就变成了1,那么`ordinal()`的值也会变成1了。
LOGIN = new AccountActionTypeEnum("LOGIN", 1);
EDIT\\_INFO = new AccountActionTypeEnum("EDIT\\_INFO", 2);
$VALUES = (new AccountActionTypeEnum[] {
REGISTER, LOGIN, EDIT\\_INFO
});
}

values方法返回类变量$VALUES[],也就是所以枚举对象的数组。我们可以通过这个方法遍历定义的三种枚举。

valueOf方法调用的是父类Enum的静态方法valueOf,返回对应name的枚举类变量。

如:

AccountActionTypeEnum.valueOf("LOGIN") =>    AccountActionTypeEnum.LOGIN

Enum源码

//实现了Comparable
public abstract class Enum\\<E extends Enum>

implements Comparable, Serializable {

<br>


private final String name; //枚举字符串名称

public final String name() {
return name;
}

private final int ordinal;//枚举顺序值

public final int ordinal() {
return ordinal;
}

//枚举的构造方法,只能由编译器调用
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}

public String toString() {
return name;
}

public final boolean equals(Object other) {
return this==other;
}

//比较的是ordinal值
public final int compareTo(E o) {
Enum other = (Enum)o;

Enum self = this;

if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;//根据ordinal值比较大小
}

@SuppressWarnings("unchecked")
public final Class getDeclaringClass() {

//获取class对象引用,getClass()是Object的方法
Class<?> clazz = getClass();
//获取父类Class对象引用
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class)clazz : (Class)zuper;

}

<br>


public static \\<T extends Enum> T valueOf(Class enumType,

String name) {
//enumType.enumConstantDirectory()获取到的是一个map集合,key值就是name值,value则是枚举变量值
//enumConstantDirectory是class对象内部的方法,根据class对象获取一个map集合的值
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}

//.....省略其他没用的方法
}

通过Enum源码,可以知道,Enum实现了Comparable接口,这也是可以使用compareTo比较的原因,当然Enum构造函数也是存在的,该函数只能由编译器调用,毕竟我们只能使用enum关键字定义枚举,其他事情就交给编译器吧。

以上是关于理解枚举类型的主要内容,如果未能解决你的问题,请参考以下文章

MySQL数据类型--------枚举与集合类型实战

JAVA枚举小结

带有红宝石集合/可枚举的酷技巧和富有表现力的片段[关闭]

关于对Enum的理解

深入理解枚举属性与for-in和for-of

[Mdfs] lc剑指 Offer 38. 字符串的排列(全排列+枚举顺序+组合类型枚举+知识理解+模板题)