目录
EnumMap分发、二维数组分发(简单但是容易出错,不安全),这里不做讲解了
1.如何用
enum Shrubbery { GROUND, CRAWLING, HANGING }
public class EnumClass {
public static void main(String[] args) {
for(Shrubbery s : Shrubbery.values()) {
print(s + " ordinal: " + s.ordinal());
printnb(s.compareTo(Shrubbery.CRAWLING) + " ");
printnb(s.equals(Shrubbery.CRAWLING) + " ");
print(s == Shrubbery.CRAWLING);
print(s.getDeclaringClass());
print(s.name());
print("----------------------");
}
// Produce an enum value from a string name:
for(String s : "HANGING CRAWLING GROUND".split(" ")) {
Shrubbery shrub = Enum.valueOf(Shrubbery.class, s);
print(shrub);
}
}
} /* Output:
GROUND ordinal: 0
-1 false false
class Shrubbery
GROUND
----------------------
CRAWLING ordinal: 1
0 true true
class Shrubbery
CRAWLING
----------------------
HANGING ordinal: 2
1 false false
class Shrubbery
HANGING
----------------------
HANGING
CRAWLING
GROUND
*///:~
上述例子中基本是enum的一个方法不细讲,同时可以直接静态调用如Shrubbery.GROUND。
2.向enum添加方法
enum除了不能继承之外,可以把其看做是一个正常的类。所以可以为其添加方法
public enum OzWitch {
// Instances must be defined first, before methods:
WEST("A"),
private String description;
// Constructor must be package or private access:
private OzWitch(String description) {
this.description = description;
}
public String getDescription() { return description; }
public static void main(String[] args) {
for(OzWitch witch : OzWitch.values())
print(witch + ": " + witch.getDescription());
}
} /* Output:
WEST: A
*///:~
也可以覆盖方法,下面覆盖上面例子toString()方法
public enum OzWitch {
// Instances must be defined first, before methods:
WEST("A"),
private String description;
// Constructor must be package or private access:
private OzWitch(String description) {
this.description = description;
}
@Override
public String toString() {
return "aaaaa";
}
public String getDescription() { return description; }
public static void main(String[] args) {
for(OzWitch witch : OzWitch.values())
print(witch + ": " + witch.getDescription());
}
} /* Output:
WEST: aaaaa
*///:~
3.switch中使用enum
enum Signal { GREEN, YELLOW, RED, }
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch(color) {
// Note that you don't have to say Signal.RED
// in the case statement:
case RED: color = Signal.GREEN;
break;
case GREEN: color = Signal.YELLOW;
break;
case YELLOW: color = Signal.RED;
break;
}
}
public String toString() {
return "The traffic light is " + color;
}
public static void main(String[] args) {
TrafficLight t = new TrafficLight();
for(int i = 0; i < 7; i++) {
print(t);
t.change();
}
}
} /* Output:
The traffic light is RED
The traffic light is GREEN
The traffic light is YELLOW
The traffic light is RED
The traffic light is GREEN
The traffic light is YELLOW
The traffic light is RED
*///:~
4.values()并不是Enum的方法
在前面第一部分说了可以使用values()进行遍历,但是当我们查看Enum源码的时候发现并没有这个方法,这是编译器给加上的,并且还加上了需要两个参数的ValueOf(),并且最后会标注上final修饰继承Enum类。
5.对Enum进行分类只能实现接口
public enum Meal2 {
APPETIZER(Food.Appetizer.class),
MAINCOURSE(Food.MainCourse.class),
DESSERT(Food.Dessert.class),
COFFEE(Food.Coffee.class);
private Food[] values;
private Meal2(Class<? extends Food> kind) {
values = kind.getEnumConstants();
}
public interface Food {
enum Appetizer implements Food {
SALAD, SOUP, SPRING_ROLLS;
}
enum MainCourse implements Food {
LASAGNE, BURRITO, PAD_THAI,
LENTILS, HUMMOUS, VINDALOO;
}
enum Dessert implements Food {
TIRAMISU, GELATO, BLACK_FOREST_CAKE,
FRUIT, CREME_CARAMEL;
}
enum Coffee implements Food {
BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
LATTE, CAPPUCCINO, TEA, HERB_TEA;
}
}
public Food randomSelection() {
return Enums.random(values);
}
public static void main(String[] args) {
for(int i = 0; i < 5; i++) {
for(Meal2 meal : Meal2.values()) {
Food food = meal.randomSelection();
System.out.println(food);
}
System.out.println("---");
}
}
}
如4所说,因为会继承Enum类并标志为final,所以我们无法继承enum,如果需要进行分类,可以像例子中的实现接口,那么
MainCourse、Dessert等都是属于Food并依据各自特点分类,进行区分,如例子。
6.EnumSet
1.传入Set的元素必须是一个enum,剩余操作跟Set没啥区别
2.EnumSet的基础是long,而一个enum实例只要1位bit,那么long有64位,超过的话,Enum可能会自动增加一个long
7.EnumMap
需要以Enum实例作为键,而可以在put的时候,改变key的值,如果key为空则返回null,而这样可以动态的改变enum实例的值对象,而常量相关的方法在编译的时候已经写死了,所以这就是EnumMap的优点,下面会说到。
8.常量相关的方法
public enum OverrideConstantSpecific {
NUT, BOLT,
WASHER {
void f() { print("Overridden method"); }
};
void f() { print("default behavior"); }
public static void main(String[] args) {
for(OverrideConstantSpecific ocs : values()) {
printnb(ocs + ": ");
ocs.f();
}
}
} /* Output:
NUT: default behavior
BOLT: default behavior
WASHER: Overridden method
*///:~
WASHER的f()就是WASHER的常量方法,并且这个常量方法可以覆盖其OverrideConstantSpecific类的f()方法。这相对于大量使用匿名类会更加方便。
9.多路分发(重点这里,说一下我的讲解)
使用enum分发
public class RoShamBo {
public static <T extends Competitor<T>>
void match(T a, T b) {
System.out.println(
a + " vs. " + b + ": " + a.compete(b));
}
public static <T extends Enum<T> & Competitor<T>>
void play(Class<T> rsbClass, int size) {
for(int i = 0; i < size; i++)
match(
Enums.random(rsbClass),Enums.random(rsbClass));
}
}
多路分发通俗点就是,多态只能在调用方法时,那么只能发生一次单路分发,而如果想a.compete(b),那么这个时候就需要在compete()方法中定义调用b进行二次分发,这就是两路分发,多路分发同理。
public enum Outcome { WIN, LOSE, DRAW } ///:~
public enum RoShamBo2 implements Competitor<RoShamBo2> {
PAPER(Outcome.DRAW, Outcome.LOSE, Outcome.WIN),
SCISSORS(Outcome.WIN, Outcome.DRAW, Outcome.LOSE),
ROCK(Outcome.LOSE, Outcome.WIN, Outcome.DRAW);
private Outcome vPAPER, vSCISSORS, vROCK;
RoShamBo2(Outcome paper,Outcome scissors,Outcome rock) {
System.out.println("调用构造器"+this.name());
this.vPAPER = paper;
this.vSCISSORS = scissors;
this.vROCK = rock;
}
@Override
public String toString() {
return super.toString();
}
public Outcome compete(RoShamBo2 it) {
switch(it) {
default:
case PAPER: return vPAPER;
case SCISSORS: return vSCISSORS;
case ROCK: return vROCK;
}
}
public static void main(String[] args) {
RoShamBo.play(RoShamBo2.class, 20);
}
}/* Output:
调用构造器PAPER
调用构造器SCISSORS
调用构造器ROCK
*///:~
对于PAPER、SCISSORS、ROCK其中放入的对象,在初始化的时候,我们要么使用常量相关方法对其中的WIN、DRAW、LOSE进行定义,或者使用构造器,所以上面需要PAPER、SCISSORS、ROCK三个enum实例,也就调用三个构造器,而参数对应的就是其中的三个参数按顺序,如果参数不对或者没有构造器如下:
编译器会通知你完善,并且,这里的
this.vPAPER = paper;
this.vSCISSORS = scissors;
this.vROCK = rock;
RoShamBo2(Outcome paper,Outcome scissors,Outcome rock) {
System.out.println("调用构造器"+this.name());
this.vPAPER = paper;
this.vSCISSORS = scissors;
this.vROCK = rock;
System.out.println(paper.name()+""+paper.ordinal()+""+vPAPER.ordinal());
}
/*调用构造器PAPER
DRAW22
调用构造器SCISSORS
WIN00
调用构造器ROCK
LOSE11
**/
第4点说了,编译器会将实例static final进行修饰,这个时候,我们看上面的输出结果,也就是将ordinal的标志位和name传给了vPAPER,而源码中那么vPAPER的实例确定下来之后就是就是paper的ordinal,name,因此
public class RoShamBo {
public static <T extends Competitor<T>>
void match(T a, T b) {
System.out.println(
a + " vs. " + b + ": " + a.compete(b));
}
public static <T extends Enum<T> & Competitor<T>>
void play(Class<T> rsbClass, int size) {
for(int i = 0; i < size; i++)
match(
Enums.random(rsbClass),Enums.random(rsbClass));
}
}
调用a.compete(b),也就是调用RoShamBo2中的compete,这时候返回Outcome输出会调去Outcome.toString(),所以这个时候
,如果不在Outcome重写toString()方法,那么返回就是在构造器已经static final成型的name,所以构造器的位置很重要,决定成果。
使用常量相关方法分发
下面就好理解多了,这里不解释了
public enum RoShamBo4 implements Competitor<RoShamBo4> {
ROCK {
public Outcome compete(RoShamBo4 opponent) {
return compete(SCISSORS, opponent);
}
},
SCISSORS {
public Outcome compete(RoShamBo4 opponent) {
return compete(PAPER, opponent);
}
},
PAPER {
public Outcome compete(RoShamBo4 opponent) {
return compete(ROCK, opponent);
}
};
Outcome compete(RoShamBo4 loser, RoShamBo4 opponent) {
return ((opponent == this) ? Outcome.DRAW
: ((opponent == loser) ? Outcome.WIN
: Outcome.LOSE));
}
public static void main(String[] args) {
RoShamBo.play(RoShamBo4.class, 20);
}
}
EnumMap分发、二维数组分发(简单但是容易出错,不安全),这里不做讲解了
10.总结(上面的都是总结哈哈)