java中枚举的基本原理

介绍

枚举是一一列举的意思,列出某个有穷集合的所有成员。java中的枚举是在jdk1.5出现的,在枚举类型没出现之前,开发人员是采用在接口中定义常量来代表枚举的。使用枚举有什么好处呢?首先见名知意,看见枚举名称,就能知道枚举所代表的意思;其次使用枚举类型可以有强制的类型约束。

枚举的基本原理

Java中的枚举都是集成子Enum这个抽象类,下面来看下这个类中有哪些方法和字段:

//所有枚举的父类
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
     //枚举常量的名称,即声明枚举时的变量名称
    private final String name;
    public final String name() {
        return name;
    }
    //顺序,枚举定义的位置,从0开始
    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;
    }
    public final int hashCode() {
        return super.hashCode();
    }
   //不支持克隆
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
    //按照声明的位置进行排序,谁声明在前,谁就排在前面
    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }
    //返回声明在哪个枚举类中
    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }
    //返回指定枚举类型,指定名称的枚举常量
    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        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);
    }
    protected final void finalize() { }
    //不支持序列化
    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize enum");
    }
    //不支持序列化
    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }
}

定义一个简单的枚举:

public enum Life{
    ANIMAL,
    PLANT
}

通过javap反编译后的代码:

public final class Life extends java.lang.Enum<Life> {
  public static final Life ANIMAL;
  public static final Life PLANT;
  public static Life[] values();
  public static Life valueOf(java.lang.String);
  static {};
}

看了反编译的代码可以知道枚举类就是一个普通的java类,枚举常量就是枚举类的实例,还为我们的枚举类添加了静态代码块和两个方法。其实还有一个字段是枚举类数组,保存了所有枚举类实例,private static final Life[] $VALUES;
下面分析下编译器为我们做了哪些事:
首先分析静态代码块;

  1. 创建Life对象实例,使用继承自Enum类的构造器,赋值给枚举实例,例如ANIMAL和PLANT,当然编译器是可以拿到枚举的名称(name)以及枚举的声明位置(ordinal)
  2. 创建Life对象数组,把所有的枚举实例添加到数组中,然后赋值给$VALUES字段

再看添加的两个方法:

  1. values方法返回$VALUES的克隆值
  2. valueOf内部调用Enum的valueOf方法,根据枚举名称返回枚举实例

有兴趣的话可以使用javap -c 具体看看详细的步骤。

枚举类与普通类的相同点

在枚举类中枚举常量最先声明,最后一个枚举常量后面加上分号,之后才能声明字段和方法。

1、为枚举类添加字段,并通过构造器赋值

//为枚举类添加description字段
public enum Life {
	//通过构造器赋值
    ANIMAL("animal"),
    PLANT("plant");
	//可以为字段添加getter和setter方法
    private String description;
	//构造默认为private的并且只能是private的
    Life(String description) {
        this.description = description;
    }
}

2、为枚举类添加方法或抽象方法,抽象方法要在枚举实例中实现

public enum Life {
    ANIMAL {
        @Override
        public void say() {
            System.out.println("animal");
        }
    },
    PLANT {
        @Override
        public void say() {
            System.out.println("plant");
        }
    };
    abstract void say();
    public void live() {
        System.out.println("live on earth");
    }
}

所以所有的枚举实例都是枚举类的子类

3、枚举类实现接口

//定义一个接口
public interface Speak {
    void say();
}
//枚举类实现接口
public enum Life implements Speak{
    ANIMAL {
        @Override
       public void say() {
            System.out.println("animal");
        }
    },
    PLANT {
        @Override
       public void say() {
            System.out.println("plant");
        }
    };
}

当然还可以声明内部类或者接口,声明常量,就不列举了

使用枚举的一个小技巧

参见下方的List和Map集合

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public enum Life {
    ANIMAL("animal"),
    PLANT("plant");
    private String classify;

    Life(String classify) {
        this.classify = classify;
    }
	//把所有的枚举类放到集合里
    private static List<Life> lives = Stream.of(values()).collect(Collectors.toList());
	//把所有的枚举按照classify进行分类转成一个map,当然可以根据其他的字段进行转换,具体情况具体分析
    private static Map<String, Life> lifeMap = Stream.of(values()).collect(Collectors.toMap(Life::getClassify, life -> life));

    public static List<Life> getLives() {
        return lives;
    }

    public String getClassify() {
        return classify;
    }

    public static Map<String, Life> getLifeMap() {
        return lifeMap;
    }
}

枚举还有两个伴生集合EnumMap和EnumSet

总结

枚举说到底还是一个java类,这也是java中枚举类强大的地方,可以添加方法和字段。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值