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實現了
Comparable
和Serializable
的介面,因此也可以使用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 綠色