1. 程式人生 > >java 列舉詳解

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 綠色