背景
继续查缺补漏,加油
1.基本enum特性
(1)下面代码演示的是enum的一些基本用法:
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);
}
}
values()返回enum实例数组;ordinal()返回一个int值,是每个enum实例在声明时的次序,从0开始;
getDeclaringClass()返回实例所属的enum类;name()返回enum实例声明时的名字;valueOf()根据
给定的名字返回相应的enum实例。
(2)静态导入enum
public enum Spiciness {
NOT,MILD,MEDIUM,HOT,FLAMING
}
---------------------------------
import static enumerated.Spiciness.*;
import static tools.Print.*;
public class Burrito {
Spiciness degree;
public Burrito(Spiciness degree){ this.degree=degree;}
public String toString(){ return "Burrito is "+degree;}
public static void main(String[] args){
print(new Burrito(NOT));
print(new Burrito(MEDIUM));
print(new Burrito(HOT));
}
}
好处是不需要再用enum类型来修饰enum实例。如果在默认包中定义enum,这种技巧无法使用。
2.向enum添加方法
package enumerated;
import static tools.Print.*;
public enum OzWitch {
//Instances must be defined first,before methods:
WEST("Miss Gulch,aka the Wicked Witch of the West"),
NORTH("Glinda,the Good Witch of the North"),
EAST("Wicked Witch of the East,wearer of the Ruby "+
"Slippers,crushed by Dorothy's house"),
SOUTH("Good by inference, but missing");
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());
}
}
如果要定义自己的方法,必须在enum实例序列的最后添加分号,而且enum实例必须先定。
我们也可以像一般的类那样覆盖enum的方法。
3.switch语句中的enum
枚举的实例的次序是固定的,通过ordinal()方法可以获取次序,所以这个次序的值就可以用在switch。
编译器已经帮我们简化了这个操作。
enum Signal {
GREEN,YELLOW,RED
}
Signal color=Signal.RED;
public void change(){
switch(color){
case RED: color=GREEN;
break;
case GREEN: color=YELLOW;
break;
case YELLOW: color=RED;
break;
}
}
4.values()的神秘之处
values是由编译器添加的static方法,还添加了valueOf方法。如果将enum实例向上转型为Enum,values方法
就不可访问了。使用Class中的getEnumConstants方法,就可以获得所有enum实例。
package enumerated;
enum Search{ HITHER,YON }
public class UpcastEnum {
public static void main(String[] args){
Search[] vals=Search.values();
Enum e=Search.HITHER; //Upcast
//e.values(); //No values() in Enum
for(Enum en:e.getClass().getEnumConstants())
System.out.println(en);
}
}
5.实现接口
enum CartoonCharacter implements Generator<CartoonCharacter>{
SLAPPY,SPANKY,PUNCHY,SILLY,BOUNCY,NUTTY,BOB;
private Random rand=new Random(47);
public CartoonCharacter next(){
return values()[rand.nextInt(values().length)];
}
}
6.随机选取
package tools;
import java.util.*;
public class Enums {
private static Random rand=new Random(47) ;
public static <T extends Enum<T>> T random(Class<T> ec){
return random(ec.getEnumConstants());
}
public static <T> T random(T[] values){
return values[rand.nextInt(values.length)];
}
}
7.使用接口组织枚举
package enumerated;
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;
}
enum Oypj implements Food{
HEELO,WORLD
}
}
这样做的好处是可以将enum中的元素进行分组,还可以保持相同类型。
将enum嵌套在另一个enum内:
package enumerated;
import tools.*;
public enum SecurityCategory {
STOCK(Security.Stock.class) ,BOND(Security.Bond.class);
Security[] values;
SecurityCategory(Class<? extends Security> kind){
values=kind.getEnumConstants();
}
}
8.使用EnumSet替代标志
替代传统的基于int的“位标志”。这种标志可以用来表示某种“开/关”信息。我们最终操作的是一些bit。
就其内部而言,它(可能)就是将一个long值作为比特向量。优点是快速高效
public enum AlarmPoints {
STAIR1,STAIR2,LOBBY,OFFICE1,OFFICE2,OFFICE3,OFFICE4,BATHROOM,UTILITY,KITCHEN
}
------------------------------------------------------------
EnumSet<AlarmPoints> points=EnumSet.noneOf(AlarmPoints.class);//Empty set
points.add(BATHROOM);
print(points);
向EnumSet添加enum实例的顺序并不重要,因为其输出的次序决定于enum实例定义时的次序
9.使用EnumMap
要求键必须是enum,EnumMap内部可以由数组来实现,所以速度很快。
命令设计模式的用法:命令模式首先需要一个只有单一方法的接口,然后从该接口实现具有各自不同的行为的多个子类。
package enumerated;
import java.util.*;
import static enumerated.AlarmPoints.*;
import static tools.Print.*;
interface Command{ void action();}
public class EnumMaps {
public static void main(String[] args){
EnumMap<AlarmPoints,Command> em=new EnumMap<AlarmPoints,Command>(AlarmPoints.class);
em.put(KITCHEN,new Command(){
public void action(){ print("Kitchen fire!");}
});
for(Map.Entry<AlarmPoints,Command> e:em.entrySet()){
printnb(e.getKey()+": ");
e.getValue().action();
}
}
}
与常量相关的方法相比,EnumMap允许改变值对象,而常量相关的方法在编译期就被固定了。
10.常量相关方法
enum的特性之一是允许为enum实例编写方法。
要实现常量相关的方法,需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象
方法。
public enum ConstantSpecificMethod {
DATE_TIME{
String getInfo(){
return DateFormat.getDateInstance().format(new Date());
}
},
VERSION{
String getInfo(){
return System.getProperty("java.version");
}
};
abstract String getInfo();
}
通过enum实例,我们可以调用其上的方法,这通常称为表驱动的代码。
通过常量相关的方法,每个enum实例可以具备自己独特的行为。
(1)使用enum的责任链:在责任链设计模式中,以多种不同的方式解决一个问题,然后将它们链接在一起。当
一个请求到来时,它遍历这个链,直到链中的某个解决方案能够处理该请求。
(2)使用enum的状态机:一个状态机可以具有有限个特定的状态,它通常根据输入,从一个状态转移到下一个
状态,不过也可能存在瞬时状态,而一旦任务执行结束,状态机就会立刻离开瞬时状态。例如:自动售货机
11.多路分发
Java只支持单路分发。也就是说,如果要执行的操作包含了不止一个类型未知的对象时,那么Java的动态绑定
机制只能处理其中的一个类型。所以必须自己来判定其他的类型,从而实现自己的动态绑定行为。
解决的办法是多路分发。如果使用两路分发,那就必须有两个方法调用:第一个方法调用决定第一个未知类型,
第二个方法调用决定第二个未知类型。一般而言,我们需要设定好某种配置,使得一个方法调用能够引出更多的
方法调用,从而能够在这个过程中处理多种类型。
例如:
public enum Outcome {
WIN,LOSE,DRAW
}
-------------------------------------------------------
package enumerated;
import java.util.*;
import static enumerated.Outcome.*;
interface Item{
Outcome compete(Item it);
Outcome eval(Paper p);
Outcome eval(Scissors s);
Outcome eval(Rock r);
}
//布
class Paper implements Item{
public Outcome compete(Item it){ return it.eval(this);}
public Outcome eval(Paper p){ return DRAW;}
public Outcome eval(Rock r){ return LOSE;}
public Outcome eval(Scissors s){ return WIN;}
public String toString(){ return "Paper";}
}
//剪刀
class Scissors implements Item{
public Outcome compete(Item it){ return it.eval(this);}
public Outcome eval(Paper p){ return LOSE;}
public Outcome eval(Scissors s){ return DRAW;}
public Outcome eval(Rock r){ return LOSE;}
public String toString(){ return "Scissors";}
}
//石头
class Rock implements Item{
public Outcome compete(Item it){ return it.eval(this);}
public Outcome eval(Paper p){ return WIN;}
public Outcome eval(Scissors s){ return LOSE;}
public Outcome eval(Rock r){ return DRAW;}
public String toString(){ return "Rock";}
}
public class RoShamBo1 {
static final int SIZE=20;
private static Random rand=new Random(47);
public static Item newItem(){
switch(rand.nextInt(3)){
default:
case 0:return new Scissors();
case 1:return new Paper();
case 2:return new Rock();
}
}
public static void match(Item a,Item b){
System.out.println(a+" vs. "+b+": "+a.compete(b));
}
public static void main(String[] args){
for(int i=0;i<SIZE;i++)
match(newItem(),newItem());
}
}
通过调用Item.compete()方法开始两路分发,先判定a的类型然后在调用compete方法的内部再判定b的类型,使用this
可以保存a的类型信息。
(1)使用enum分发
package enumerated;
import tools.*;
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));
}
}
-------------------------------------------------------
package enumerated;
import static enumerated.Outcome.*;
public enum RoShamBo2 implements Competitor<RoShamBo2> {
PAPER(DRAW,LOSE,WIN),
SCISSORS(WIN,DRAW,LOSE),
ROCK(LOSE,WIN,DRAW);
private Outcome vPAPER,vSCISSORS,vROCK;
RoShamBo2(Outcome paper,Outcome scissors,Outcome rock){
this.vPAPER=paper;
this.vSCISSORS=scissors;
this.vROCK=rock;
}
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);
}
}
第一次分发是compete方法的调用,第二次分发是switch的调用。
(2)使用常量相关的方法
package enumerated;
import static enumerated.Outcome.*;
public enum RoShamBo3 implements Competitor<RoShamBo3> {
PAPER{
public Outcome compete(RoShamBo3 it){
switch(it){
default: //To placate the compiler
case PAPER:return DRAW;
case SCISSORS:return LOSE;
case ROCK:return WIN;
}
}
},
SCISSORS{
public Outcome compete(RoShamBo3 it){
switch(it){
default: //To placate the compiler
case PAPER:return WIN;
case SCISSORS:return DRAW;
case ROCK:return LOSE;
}
}
},
ROCK{
public Outcome compete(RoShamBo3 it){
switch(it){
default: //To placate the compiler
case PAPER:return LOSE;
case SCISSORS:return WIN;
case ROCK:return DRAW;
}
}
};
public abstract Outcome compete(RoShamBo3 it);
public static void main(String[] args){
RoShamBo.play(RoShamBo3.class, 20);
}
}
再简化一下:
package enumerated;
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);
}
}
(3)使用EnumMap分发
package enumerated;
import java.util.*;
import static enumerated.Outcome.*;
public enum RoShamBo5 implements Competitor<RoShamBo5>{
PAPER,SCISSORS,ROCK;
static EnumMap<RoShamBo5,EnumMap<RoShamBo5,Outcome>>
table=new EnumMap<RoShamBo5,EnumMap<RoShamBo5,Outcome>>(RoShamBo5.class);
static{
for(RoShamBo5 it:RoShamBo5.values())
table.put(it,new EnumMap<RoShamBo5,Outcome>(RoShamBo5.class));
initRow(PAPER,DRAW,LOSE,WIN);
initRow(SCISSORS,WIN,DRAW,LOSE);
initRow(ROCK,LOSE,WIN,DRAW);
}
static void initRow(RoShamBo5 it,Outcome vPAPER,Outcome vSCISSORS,Outcome vROCK){
EnumMap<RoShamBo5,Outcome> row=RoShamBo5.table.get(it);
row.put(RoShamBo5.PAPER,vPAPER);
row.put(RoShamBo5.SCISSORS,vSCISSORS);
row.put(RoShamBo5.ROCK,vROCK);
}
public Outcome compete(RoShamBo5 it){
return table.get(this).get(it);
}
public static void main(String[] args){
RoShamBo.play(RoShamBo5.class,20);
}
}
(4)使用二维数组
package enumerated;
import static enumerated.Outcome.*;
public enum RoShamBo6 implements Competitor<RoShamBo6> {
PAPER,SCISSORS,ROCK;
private static Outcome[][] table={
{DRAW,LOSE,WIN},
{WIN,DRAW,LOSE},
{LOSE,WIN,DRAW},
};
public Outcome compete(RoShamBo6 other){
return table[this.ordinal()][other.ordinal()];
}
public static void main(String[] args){
RoShamBo.play(RoShamBo6.class, 20);
}
}
总结
最大的收获是学习了几个设计模式还有enum的各种用法。