java 枚举详解

1. 概述

枚举(enum)全写为的全称为:enumeration。是jdk1.5才新引进的概念,在Java中enum的有与C、C++相似的基本用法,也有很多扩展的用法。

尽管枚举类型看着像一种新的数据类型,但实际上它是一种受限制的类(继承自java.lang.Enum)。

public enum ColorEnum{
    //相当于创建了5个实例,调用了5次 Enum(String name, int ordinal)
    RED,WHITE,BLUE,BLACK,GREEN
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

经过编译器编译后产生的是一个class文件,该class文件经过反编译软件编译后可以看到实际上生成了一个类:

public class com.com.yarward.design.ColorEnum extends java.lang.Enum{
    public static final com.com.yarward.design.ColorEnum RED;
    public static final com.com.yarward.design.ColorEnum WHITE;
    public static final com.com.yarward.design.ColorEnum BLUE;
    public static final com.com.yarward.design.ColorEnum BLACK;
    public static final com.com.yarward.design.ColorEnum GREEN;
    static {};
    public static com.com.yarward.design.ColorEnum[] values();
    public static com.com.yarward.design.ColorEnum valueOf(java.lang.String);
    ....
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
/**
 * This is the common base class of all Java language enumeration types.
 *
 * <p> Note that when using an enumeration type as the type of a set
 * or as the type of the keys in a map, specialized and efficient
 * {@linkplain java.util.EnumSet set} and {@linkplain
 * java.util.EnumMap map} implementations are available.
 *
 * @since   1.5
 */
public abstract class Enum<E extends Enum<E>>
  implements Comparable<E>, Serializable {...}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2. 特性介绍

2.1 基础用法

在未引入enum之前,我们定义常量的时候总是使用这样的方式:public final static 类型....,在引入了枚举类型之后,我们可以将一组相似属性统一组织、管理管理,并包含一些自己的方法:

  • name():返回枚举常量的名称。

  • ordinal():返回枚举常量的时序,0、1、2……。

  • getDeclaringClass():返回实例所属的enum类型。

  • equals(Object other):判断两个enum实例是否相等,也可用用==号来进行判断。

    注意不能用直接用数字常量与枚举类型直接比较。

  • 因为父类Enum实现了ComparableSerializable的接口,因此也可以使用compareTo()方法。

例:

ColorEnum colorEnumWhite = ColorEnum.WHITE;
ColorEnum colorEnumRed = ColorEnum.RED;

Log.d("test enum","enum colorEnumRed.toString() is " + colorEnumRed.toString());
Log.d("test enum","enum colorEnumWhite is " + colorEnumWhite);
Log.d("test enum","enum constant name is " + colorEnumWhite.name());
Log.d("test enum","enum constant ordinal is " + colorEnumWhite.ordinal());
Log.d("test enum","enum getDeclaringClass is " + colorEnumWhite.getDeclaringClass());
Log.d("test enum","enum colorEnumWhite = colorEnumRed " + colorEnumWhite.equals(colorEnumRed));
Log.d("test enum","enum colorEnumWhite = 1 " + colorEnumWhite.equals(1));
Log.d("test enum","enum colorEnumWhite compareTo colorEnumRed " + colorEnumWhite.compareTo(colorEnumRed));
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

输出结果:

enum colorEnumRed.toString() is RED
enum colorEnumWhite is WHITE
constant name is WHITE
constant ordinal is 1
getDeclaringClass is class com.yarward.design.MainActivity$ColorEnum
enum colorEnumWhite = colorEnumRed false
enum colorEnumWhite = 1 false
enum colorEnumWhite compareTo colorEnumRed 1
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在使用enum时需要注意:

定义的枚举类型不可再继承其他的类型的类,因为它已经继承了java.lang.Enum

2.2 添加成员、方法

前面介绍过,枚举也是一种类,是一种受限制的类,不能在继承其他任何类的类。因此它也具有类应该有的特性。

java中的枚举与c/c++中的枚举不同,c/c++可以显示的为枚举类型赋值,而java中的枚举不可以。

/*C/C++可以显示的给枚举类型赋值*/
typedef enum _Number{
  one = 1,
  two,
  three,
  ten = 10
} Number;
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Java虽然不能直接为实例赋值,但是它有更优秀的解决方案:为 enum 添加方法来间接实现显示赋值。枚举类可以有自己的成员、构造方法、普通方法、抽象方法、静态方法。

唯一用起来比较别扭的一点是,enum相当于把类的实例化(枚举常量)放到了类声明的内部,这些枚举常量也可以重写枚举类型中的方法。

 public enum WeekEnum{
     //定义枚举常量
     Mon(true),Tue(true),Wen(true),Thu(true),
       //定义枚举常量的时候可以重写枚举类型中方法
       Fri(true){
         @Override
         public String helloEnum() {
             return "sad to work";
         }
     },
     Sat(false),Sun(false);

     //枚举类型中可以有成员变量
     private boolean mIsWork;

     //枚举类型中可以有构造函数
     private WeekEnum(boolean isWork){
         this.mIsWork = isWork;
     }

     //枚举类型中可以有成员方法
     public boolean isWork(){
         return mIsWork;
     }

     public String helloEnum(){
         return "happy to work";
     }

     //枚举类型中可以静态方法
     public static String infoPrint(){
         return "Monday to Sunday!";
     }
 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
WeekEnum tue = WeekEnum.Tue;
WeekEnum fri = WeekEnum.Fri;
WeekEnum sat = WeekEnum.Sat;

Log.d("test enum","enum static method :" + WeekEnum.infoPrint());
Log.d("test enum","enum tue.helloEnum :" + tue.helloEnum());
Log.d("test enum","enum fri.helloEnum :" + fri.helloEnum());
Log.d("test enum","enum fri is work? " + fri.isWork());
Log.d("test enum","enum sat is work? " + sat.isWork());
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
enum static method :Monday to Sunday! 
enum tue.helloEnum :happy to work      
enum fri.helloEnum :sad to work        
enum fri is work? true                 
enum sat is work? false                
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

2.3 实现接口

同样作为类,枚举类型也可以实现接口。用法和普通类的用法一致。

public interface IDoSomeThing{
    public void doJobs();
}
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
public enum WeekEnum implements IDoSomeThing{
    Mon(true),Tue(true),Wen(true),Thu(true),Fri(true){
        @Override
        public String helloEnum() {
            return "sad to work";
        }
    },
    Sat(false){
        @Override
        public void doJobs() {
            Log.d("test enum","I am at resting");
        }
    },Sun(false);
  ...
  ...
    @Override
    public void doJobs() {
        Log.d("test enum","I am at working");
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
fri.doJobs();
sat.doJobs();
 
 
  • 1
  • 2
  • 1
  • 2
I am at working
I am at resting
 
 
  • 1
  • 2
  • 1
  • 2

3、枚举的典型应用场景

3 .1 常量

如果enum不添加任何方法的话,枚举的默认值为:从0开始的有序数值,每次递增1

如果定义的枚举类型没有任何方法的话,可以在最后一个实例(常量)后加”,” 或”;” 或什么都不加。

注意:如果枚举类中添加了方法,则必须在最后一个实例后添加”;”

以下三种方式定义都可以:

enum ColorEnum{ RED, WHITE, BLUE, BLACK, GREEN }
enum ColorEnum{ RED, WHITE, BLUE, BLACK, GREEN, }
enum ColorEnum{ RED, WHITE, BLUE, BLACK, GREEN; }
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

3.2 switch中

enum Signal {  
    GREEN, YELLOW, RED  
}  
public class TrafficLight {  
    Signal color = Signal.RED;  
    public void change() {  
        switch (color) {  
        case RED:  
            color = Signal.GREEN;  
            break;  
        case YELLOW:  
            color = Signal.RED;  
            break;  
        case GREEN:  
            color = Signal.YELLOW;  
            break;  
        }  
    }  
}  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

3.3 组织枚举

可以将类型相近的枚举通过接口或类组织起来,但是一般用接口方式进行组织。

原因是:Java接口在编译时会自动为enum类型加上public static修饰符;Java类在编译时会自动为 enum 类型加上static修饰符。看出差异了吗?

没错,就是说,在类中组织 enum,如果你不给它修饰为 public,那么只能在本包中进行访问。

在接口中不加public static修饰词,也能在其他包中被引用。

public interface INumberEnum{
    public int getCode();
    public String getDescription();
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
public interface Plant {
    enum Vegetable implements INumberEnum {
        POTATO(0, "土豆"),
        TOMATO(0, "西红柿");

        Vegetable(int number, String description) {
            this.code = number;
            this.description = description;
        }

        private int code;
        private String description;

        @Override
        public int getCode() {
            return 0;
        }

        @Override
        public String getDescription() {
            return null;
        }
    }

    enum Fruit implements INumberEnum {
        APPLE(0, "苹果"),
        ORANGE(0, "桔子"),
        BANANA(0, "香蕉");

        Fruit(int number, String description) {
            this.code = number;
            this.description = description;
        }

        private int code;
        private String description;

        @Override
        public int getCode() {
            return 0;
        }

        @Override
        public String getDescription() {
            return null;
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

3.4 策略枚举(没用过)

这种枚举通过枚举嵌套枚举的方式,将枚举实例(常量)分类处理,虽然没有通过switch语句使用简洁,但是更加灵活。

enum PayrollDay {
    MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(
            PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(PayType.WEEKDAY), SATURDAY(
            PayType.WEEKEND), SUNDAY(PayType.WEEKEND);

    private final PayType payType;

    PayrollDay(PayType payType) {
        this.payType = payType;
    }

    double pay(double hoursWorked, double payRate) {
        return payType.pay(hoursWorked, payRate);
    }

    // 策略枚举
    private enum PayType {
        WEEKDAY {
            double overtimePay(double hours, double payRate) {
                return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT)
                        * payRate / 2;
            }
        },
        WEEKEND {
            double overtimePay(double hours, double payRate) {
                return hours * payRate / 2;
            }
        };
        private static final int HOURS_PER_SHIFT = 8;

        abstract double overtimePay(double hrs, double payRate);

        double pay(double hoursWorked, double payRate) {
            double basePay = hoursWorked * payRate;
            return basePay + overtimePay(hoursWorked, payRate);
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
Log.d("test enum","时薪100的人在周五工作8小时的收入:" + PayrollDay.FRIDAY.pay(8.0, 100));
Log.d("test enum","时薪100的人在周六工作8小时的收入:" + PayrollDay.SATURDAY.pay(8.0, 100));
 
 
  • 1
  • 2
  • 1
  • 2

3.5 EnumSet和EnumMap

EnumSet 是枚举类型的高性能Set实现。它要求放入它的枚举常量必须属于同一枚举类型。它的使用与其他的set没有什么不同,只是在构造的时候有一些特殊而已。

public void showEnumSetValue(EnumSet<ColorEnum> enumSet){
    Log.d("enum test","enum set value is : "+ Arrays.toString(enumSet.toArray()));
}
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
//创建一个之类enum类型的空集合
EnumSet<ColorEnum> enumEnumSet = EnumSet.noneOf(ColorEnum.class);
showEnumSetValue(enumEnumSet);
enumEnumSet.add(ColorEnum.BLACK);
enumEnumSet.add(ColorEnum.BLUE);
showEnumSetValue(enumEnumSet);

//创建一个指定enum类型的所有实例的集合
EnumSet<ColorEnum> enumEnumSet1 = EnumSet.allOf(ColorEnum.class);
showEnumSetValue(enumEnumSet1);

//创建一个指定enum初始化内容的集合
EnumSet<ColorEnum> enumEnumSet2 = EnumSet.of(ColorEnum.GREEN,ColorEnum.WHITE);
showEnumSetValue(enumEnumSet2);

//创建指定类型,指定范围的集合,包含边界数据
EnumSet<ColorEnum> enumEnumSet3 = EnumSet.range(ColorEnum.BLUE,ColorEnum.GREEN);
showEnumSetValue(enumEnumSet3);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
enum set value is : []
enum set value is : [BLUE, BLACK]
enum set value is : [RED, WHITE, BLUE, BLACK, GREEN]
enum set value is : [WHITE, GREEN]
enum set value is : [BLUE, BLACK, GREEN]
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

EnumMap 是专门为枚举类型量身定做的Map实现。虽然使用其它的Map实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用EnumMap会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值。这使得EnumMap的效率非常高。

EnumMap<ColorEnum, String> enumStringEnumMap = new EnumMap<ColorEnum, String>(ColorEnum.class);
enumStringEnumMap.put(ColorEnum.RED,"红色");
enumStringEnumMap.put(ColorEnum.BLUE,"蓝色");
enumStringEnumMap.put(ColorEnum.GREEN,"绿色");

for(Map.Entry<ColorEnum,String> enumStringEntry : enumStringEnumMap.entrySet()){
    Log.d("enum test","enum map key: " +enumStringEntry.getKey().name()+ " value " + enumStringEntry.getValue());
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
enum map key: RED value 红色
enum map key: BLUE value 蓝色
enum map key: GREEN value 绿色
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值