测者的测试技术手册:Junit单元测试遇见的一个枚举类型的坑(枚举类型详解)

时间:2023-03-08 16:24:20

Enum的简介

枚举类型很早就在计算机语言中存在了,主要被用来将一组相似的值包含进一种类型中,这种类型的名称被定义成独一无二的类型描述符,这就是枚举类型。

在java语言中,枚举类型是一个完整功能的类,允许开发者给枚举类型添加方法和属性,同时也可以提供接口。同时Java也为Enum提供了高质量的实现,比如comparable和Serializable接口.

* 其中:Comparable 是排序接口,如果一个Java类有这个接口,那么只能说明这个类支持排序。即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组),则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。 Serializable接口的对象转换成一个字符序列,并能够在以后将这个字节序列完全恢复为原来的对象。而且这个过程是可以通过网络完成的,也就是说序列化机制能够自动弥补不同操作系统之间差异。*

Enum的定制

通过类的定义,可以给Enum一些复杂的功能,例如下面代码给Enum增加属性:

public enum DrinkEnum{

  1. // 酒

  2. Wine("a cup of wine ");

  3. //橙汁

  4. Oragen_Juice("a cup of oragen juice")

  5. //Enum对象的drink_class属性

  6. private String drink_class;

  7. //枚举对象构造函数

  8. private DrinkEnum(String drink){

  9.    this.drink_class=drink;

  10. }

  11. //枚举对象的drink_class属性

  12. public String getDrink(){

  13.    return this.drink_class;

  14. }

Enum的一些坑

  1. 1 Enum 不支持public和protected修饰符的构造方法,因此构造函数一定要是private或者friendly的,这也就约束了枚举对象是无法在程序中通过直接调用构造函数进行初始化的。

  2. 2 Enum的值是通过运行期构造出来的对象表示的,因此在集群环境下,每个jvm构造出同义对象,但是在做布尔运算的时候有可能有问题,因此要格外注意。在对Enum进行比较的时候,使用值比对。

Enum的原理

在使用enum创建枚举类型后,编译器会为其生成一个对应的枚举类,这个类继承与java.lang.Enum。举例如下:

  1. enum Day {

  2.    MONDAY, TUESDAY, WEDNESDAY,

  3.    THURSDAY, FRIDAY, SATURDAY, SUNDAY

  4. }

编译后会变成Day.class,那么测者反编译后看到如下:

  1. final class Day extends Enum

  2. {

  3.    //编译器为我们添加的静态的values()方法

  4.    public static Day[] values()

  5.    {

  6.        return (Day[])$VALUES.clone();

  7.    }

  8.    //编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法

  9.    public static Day valueOf(String s)

  10.    {

  11.        return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);

  12.    }

  13.    //私有构造函数

  14.    private Day(String s, int i)

  15.    {

  16.        super(s, i);

  17.    }

  18.     //前面定义的7种枚举实例

  19.    public static final Day MONDAY;

  20.    public static final Day TUESDAY;

  21.    public static final Day WEDNESDAY;

  22.    public static final Day THURSDAY;

  23.    public static final Day FRIDAY;

  24.    public static final Day SATURDAY;

  25.    public static final Day SUNDAY;

  26.    private static final Day $VALUES[];

  27.    static

  28.    {    

  29.        //实例化枚举实例

  30.        MONDAY = new Day("MONDAY", 0);

  31.        TUESDAY = new Day("TUESDAY", 1);

  32.        WEDNESDAY = new Day("WEDNESDAY", 2);

  33.        THURSDAY = new Day("THURSDAY", 3);

  34.        FRIDAY = new Day("FRIDAY", 4);

  35.        SATURDAY = new Day("SATURDAY", 5);

  36.        SUNDAY = new Day("SUNDAY", 6);

  37.        $VALUES = (new Day[] {

  38.            MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY

  39.        });

  40.    }

  41. }

可以看到,编译后生成一个final类,同时前面的七个日期定义成七个类型。同时还有两个静态方法,分别是values()和 valueOf(),MONDAY枚举类型对应public static final Day MONDAY;,values()方法的作用就是获取枚举类中的所有变量,并作为数组返回,而valueOf(String name)方法与Enum类中的valueOf方法的作用类似根据名称获取枚举变量,只不过编译器生成的valueOf方法更简洁些只需传递一个参数。由于values()方法是由编译器插入到枚举类中的static方法,所以如果我们将枚举实例向上转型为Enum,那么values()方法将无法被调用,因为Enum类中并没有values()方法,valueOf()方法也是同样的道理。

final变量经常和static关键字一起使用,作为常量。final类通常功能是完整的,不允许被继承。

因此枚举类型,在调用中如果对内部变量使用了set函数,那么就是对一个常量进行了上set操作,也就会导致所有调用的地方的值都发生了变化。

EnumSet和EnumMap

EnumSet

EnumSet是一个针对枚举类型的高性能Set接口实现,但是在其中装入的枚举类型必须是同类型的,在EnumSet中通过bit-vector实现,也就是一个long型。EnumSet支持在遍历。

  1. for (WeekDayEnum day:EnumSet.range(WeekDayEnum.Mon,WeekDayEnum.Sun)){

  2.    System.out.println(day)

  3. }

同时EnumSet还提供了一个获取子集的方法:

  1. EnumSet <WeekDayEnum> JobDays =  EnumSet.of(WeekDayEnum.Mon,WeekDayEnum.Fri);

EnumMap

EnumMap是一个高性能的Map接口实现,主要管理用枚举做Key-Value的关系,内部是通过数组方式实现的。

  1. private static Map<WeekDayEnum,RainbowClolor> schema = new EnumMap<WeekDayEnum.values()[i],RainbowClolor.values()[i]>(WeekDatEnum.class);

关注测者,关注测试