19.1 基本enum特性
package Ch19;
import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;
enum Shrubbery{ GROUND, CRAWLING, HANGING }
public class EnumClass {
public static void main(String[] args) {
for(Shrubbery s : Shrubbery.values()){
//ordinal()方法返回enum实例声明时的次序。
print(s+" ordianl: "+s.ordinal());
printnb(s.equals(Shrubbery.CRAWLING)+" ");
print(s==Shrubbery.CRAWLING);
//返回声明时的类
print(s.getDeclaringClass());
//enum实例的名字
print(s.name());
print("-------------------");
}
for(String s : "HANGING CRAWLING GROUND".split(" ")){
//静态方法valueOf,返回enum类中给的名字的实例
Shrubbery shrub = Enum.valueOf(Shrubbery.class, s);
print(shrub);
}
}
}
/*GROUND ordianl: 0
false false
class Ch19.Shrubbery
GROUND
-------------------
CRAWLING ordianl: 1
true true
class Ch19.Shrubbery
CRAWLING
-------------------
HANGING ordianl: 2
false false
class Ch19.Shrubbery
HANGING
-------------------
HANGING
CRAWLING
GROUND
*/
19.1.1 将静态导入用于enum
//非静态导入
package Ch19;
enum Spiciness{NOT, MILD, MEDIUM, HOT, FLAMING}
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) {
System.out.println(new Burrito(Spiciness.NOT));
System.out.println(new Burrito(Spiciness.MEDIUM));
System.out.println(new Burrito(Spiciness.HOT));
}
}
package Ch19;
public enum Spiciness{NOT, MILD, MEDIUM, HOT, FLAMING}
package Ch19;
import static enumerated.Spiciness.*;
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) {
System.out.println(new Burrito(NOT));
System.out.println(new Burrito(MEDIUM));
System.out.println(new Burrito(HOT));
}
}
/*Burrito is NOT
Burrito is MEDIUM
Burrito is HOT*/
19.2 向enum中添加新方法
不能继承enum。enum和class是同级关键词。
19.3 switch语句中的enum
package Ch19;
import static tools.Print.print;
enum Signal{GREEN,YELLOW,RED}
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch (color) {
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();
}
}
}
19.4 values的神秘之处
19.5 实现而非继承
package Ch19;
import Ch15.Generator;
import java.util.*;
import Ch7.Cartoon;
import net.mindview.util.*;
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)];
}
}
public class EnumImplementation {
public static<T> void printNext(Generator<T> rg){
System.out.print(rg.next()+". ");
}
public static void main(String[] args) {
CartoonCharacter cc = CartoonCharacter.BOB;
for(int i=0; i<10; i++)
printNext(cc);
}
}
/*BOB. PUNCHY. BOB. SPANKY. NUTTY. PUNCHY. SLAPPY. NUTTY. NUTTY. SLAPPY. */
19.6 随机选取
package Ch19;
import java.util.Random;
public class Enums {
private static Random rand = new Random(47);
// <T extends Enum<T>>表示T是enum的一个实例
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)];
}
}
package Ch19;
enum Activity{ SITTING, LYING, STANDING, HOPPING, RUNNING, DODGING, JUMPING, FALLING,FLYING}
public class RandomTest {
public static void main(String[] args) {
for(int i=0; i<3; i++)
System.out.print(Enums.random(Activity.class)+" ");
}
}
19.7 使用接口组织枚举
package Ch19;
public interface Food {
enum Appetizer implements Food {
sALAD, SOUP, SPRING_ROLL5;
}
enum MainCourse implements Food {
LASAGNE,BURRITO;
}
}
package Ch19;
public class TypeOfFood {
public static void main(String[] args) {
Food food = Food.Appetizer.sALAD;
food = Food.MainCourse.LASAGNE;
}
}
package Ch19;
public enum Course { APPETIZER(Food.Appetizer.class),MAINCOURESE(Food.MainCourse.class),COFFEE(Food.Coffee.class);
private Food[] values;
//构造器
private Course(Class<? extends Food> kind) {
// 方法返回枚举的实例
values = kind.getEnumConstants();
}
public Food randomSelection(){
//随机返回数组中元素
return Enums.random(values);
}
}
public class Meal {
public static void main(String[] args) {
for(int i=0; i<5; i++){
for(Course course : Course.values()){
Food food = course.randomSelection();
System.out.println(food);
}
System.out.println("---");
}
}
}
/*
SPRING_ROLL5
VINDALOO
HERB_TEA
---
SPRING_ROLL5
BURRITO
ESPRESSO
---
SOUP
PAD_THAT
TEA
---
SOUP
LASAGNE
TEA
---
sALAD
BURRITO
LATTE
---
*/
/*将一个enum嵌套在另外一个enum内*/
package Ch19;
import net.mindview.util.*;
public enum SecurityCategory {
STOCK(Security.Stock.class), BOND(Security.Bond.class);
Security[] values;
//构造器
SecurityCategory(Class<? extends Security> kind) {
values = kind.getEnumConstants();
}
//将Stock和Bond组织成公共类型。
interface Security {
enum Stock implements Security {SHORT, LONG, MARGIN}
enum Bond implements Security {MUNICIPAL, JUNK}
}
public Security randomSelection() {
return Enums.random(values);
}
public static void main(String[] args) {
for(int i= 0; i<10; i++){
//
SecurityCategory category = Enums.random(SecurityCategory.class);
System.out.println(category+": "+category.randomSelection());
}
}
}
/*BOND: MUNICIPAL
BOND: MUNICIPAL
STOCK: MARGIN
STOCK: MARGIN
BOND: JUNK
STOCK: SHORT
STOCK: LONG
STOCK: LONG
BOND: MUNICIPAL
BOND: JUNK
*/
19.8 使用EnumSet替代标志
EnumSet的基础是long,一个long有64位,而一个enum实例只需一位表示存在。最多使用不超过64个元素的enum(实际可以)。
package Ch19;
import java.util.*;
import static Ch19.AlarmPoints.*;
import static net.mindview.util.Print.print;
public class EnumSets {
public static void main(String[] args) {
EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class);
points.add(BATHROOM);
print(points);
points.addAll(EnumSet.of(STAIR1,STAIR2,KITCHEN));
print(points);
points = EnumSet.allOf(AlarmPoints.class);
print(points);
points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
print(points);
points = EnumSet.complementOf(points);
print(points);
}
}
/*[BATHROOM]
[STAIR1, STAIR2, BATHROOM, KITCHEN]
[STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY, KITCHEN]
[LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY]
[STAIR1, STAIR2, KITCHEN]*/
19.9 使用EnumMap
package Ch19;
import java.util.*;
import static Ch19.AlarmPoints.*;
import static net.mindview.util.Print.*;
interface Command {void action();}
public class EnumMaps {
public static void main(String[] args) {
//EnumMap要求Key必须来自一个enum,EnumMao内部是用数组实现。
EnumMap<AlarmPoints,Command> em = new EnumMap<>(AlarmPoints.class);
em.put(KITCHEN, new Command(){
public void action(){
print("Kitchen fire!");
}
});
em.put(BATHROOM, new Command(){
public void action(){
print("Bathroom alert!");
}
});
for(Map.Entry<AlarmPoints,Command> e : em.entrySet()){
printnb(e.getKey() +": ");
e.getValue().action();
}
try {
em.get(UTILITY).action();
}catch (Exception e){
print(e);
}
}
}
/*BATHROOM: Bathroom alert!
KITCHEN: Kitchen fire!
java.lang.NullPointerException*/
抛出的空指针异常源于enum实例作为一个键总是存在,默认值为null
19.10 常量相关方法
enum允许为enum实例编写方法。需要你为enum定义一个或多个abstract方法,如何为每个enum实例实现该抽象方法。
package Ch19;
// table-driven-code 通过相应的enum实例,可以调用其上的方法。
import java.text.DateFormat;
import java.util.Date;
public enum ConstantSpecificMethod {
DATE_TIME {
String getInfo() {
return DateFormat.getDateInstance().format(new Date());
}
},
CLASSPATH{
String getInfo(){
return System.getenv("CLASSPATH");
}
},
VERSION {
String getInfo() {
return System.getProperty("java.version");
}
};
abstract String getInfo();
public static void main(String[] args) {
for(ConstantSpecificMethod csm : values())
System.out.println(csm.getInfo());
}
}
/*2017-11-17
F:\TIJ4\code;
1.8.0_131*/
package Ch19;
import java.util.EnumSet;
public class CarWash {
public enum Cycle {
UNDERBODY{
void action(){
System.out.println("Spraying");
}
},
WHEELWASH{
void action(){
System.out.println("Washing");
}
},
PREWASH{
void action(){
System.out.println("Loosening");
}
},
BASIC{
void action(){
System.out.println("The basic wash");
}
},
HOTWAX{
void action(){
System.out.println("Applying");
}
},
RINSE{
void action(){
System.out.println("Rinsing");
}
},
BLOWDRY{
void action(){
System.out.println("Blowing dry");
}
};
abstract void action();
}
EnumSet<Cycle> cycles = EnumSet.of(Cycle.BASIC,Cycle.RINSE);
public void add(Cycle cycle){cycles.add(cycle);}
public void washCar(){
for(Cycle c : cycles)
c.action();
}
//CarWash的toString调用了cyclyes的toString方法。
public String toString(){return cycles.toString();}
public static void main(String[] args) {
CarWash wash = new CarWash();
System.out.println(wash);
wash.washCar();
wash.add(Cycle.BLOWDRY);
//Set重复调用被忽略
wash.add(Cycle.BLOWDRY);
wash.add(Cycle.RINSE);
wash.add(Cycle.HOTWAX);
System.out.println(wash);
wash.washCar();
}
}
/*[BASIC, RINSE]
The basic wash
Rinsing
[BASIC, HOTWAX, RINSE, BLOWDRY]
The basic wash
Applying
Rinsing
Blowing dry*/
19.10.1 使用enum的职责链(chain of Responsibility)
职责链,程序员以多种不同方法来解决一个问题,然后把他们链接在一起。当一个请求到来时,它遍历这个链,直到链中某个解决方法能够处理该请求。
package Ch19;
import java.util.Iterator;
//定义邮件
class Mail {
//定义邮件可以确认的信息属性。
enum GeneralDelivery{YES,NO1,NO2,NO3,NO4,NO5}
enum Scannability{UNSCANNABLE,YES1,YES2,YES3,YES4}
enum Readability {ILLEGIBLE,YES1,YES2,YES3,YES4}
enum Address{INCORRECT,OK1,OK2,OK3,OK4,OK5,OK6}
enum ReturnAddress{MISSING,OK1,OK2,OK3,OK4,OK5}
// Mail的属性,留待初始化。
GeneralDelivery generalDelivery;
Scannability scannability;
Readability readability;
Address address;
ReturnAddress returnAddress;
static long counter = 0;
long id = counter++;
public String toString(){return "Mail "+id;}
//打印初始化的信息。
public String details(){
return toString() +
", General Delivery: "+generalDelivery
+", Address Scanability: "+scannability
+", Address Readablity: "+readability
+", Address Address: "+address
+", Return address: "+returnAddress;
}
//不是类名的初始化函数。利用Enums的random函数。
public static Mail randomMail() {
Mail m = new Mail();
m.generalDelivery = Enums.random(GeneralDelivery.class);
m.scannability = Enums.random(Scannability.class);
m.readability = Enums.random(Readability.class);
m.address = Enums.random(Address.class);
m.returnAddress = Enums.random(ReturnAddress.class);
return m;
}
//generato对象生成器。调用上面random函数。
public static Iterable<Mail> generator(final int count) {
return new Iterable<Mail>() {
int n = count;
public Iterator<Mail> iterator() {
return new Iterator<Mail>(){
public boolean hasNext(){return n-- >0;}
public Mail next() {return randomMail();}
public void remove(){throw new UnsupportedOperationException();}
};
}
};
}
}
//邮局
public class PostOffice {
//将不同的邮件的处理方法组织成职责链。
enum MailHandler {
GENERAL_DELIVERY {
boolean handle(Mail m){
switch(m.generalDelivery) {
case YES:
System.out.println("Using general delivery for "+m);
return true;
default:return false;
}
}
},
MACHINE_SCAN{
boolean handle(Mail m) {
switch(m.scannability) {
case UNSCANNABLE:return false;
default:
switch (m.address) {
case INCORRECT:return false;
default:
System.out.println("Delivering "+m+ " automatically");
return true;
}
}
}
},
VISUAL_INSPECTION {
boolean handle(Mail m) {
switch (m.readability){
case ILLEGIBLE:return false;
default:
switch (m.address) {
case INCORRECT:return false;
default:
System.out.println("Delivering "+m+" normally");
return true;
}
}
}
},
RETURN_TO_SENDER {
boolean handle(Mail m) {
switch (m.returnAddress){
case MISSING : return false;
default:
System.out.println("Returning "+m+" to sender");
return true;
}
}
};
abstract boolean handle(Mail m);
}
//对于每一封邮件,按照职责链匹配职责。
static void handle(Mail m) {
for(MailHandler handler : MailHandler.values())
if(handler.handle(m))
return;
System.out.println(m+" is a dead letter");
}
//测试
public static void main(String[] args) {
for(Mail mail : Mail.generator(3)) {
System.out.println(mail.details());
handle(mail);
System.out.println("*****");
}
}
}
/*Mail 0, General Delivery: NO2, Address Scanability: UNSCANNABLE, Address Readablity: YES3, Address Address: OK1, Return address: OK1
Delivering Mail 0 normally
*****
Mail 1, General Delivery: NO5, Address Scanability: YES3, Address Readablity: ILLEGIBLE, Address Address: OK5, Return address: OK1
Delivering Mail 1 automatically
*****
Mail 2, General Delivery: YES, Address Scanability: YES3, Address Readablity: YES1, Address Address: OK1, Return address: OK5
Using general delivery for Mail 2
*****
*/
19.10.2 使用enum状态机??
枚举类型非常适合用来创建状态机
19.11 多路分发
Java只支持单路分发–也就是说,如果要执行的操作包含了不止一个类型未知的对象时,那么Java的动态绑定机制只能处理其中一个的类型。你必须自己判断其他的类型,从而实现自己的动态绑定行为。
解决方法–多路分发,必须为每个类型提供一个实际的方法调用。
package Ch19;
//所谓类型未知,常指一个基类有很多导出类,调用方法时,不知具体导出类型,只知道基类。
//变量被声明时的类型叫做变量的静态类型(Static Type) 又叫明显类型(Apparent Type)。变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)。
import static Ch19.Outcome.*;
import java.util.*;
interface Item{
Outcome compete(Item it);
Outcome eval(Paper p);
Outcome eval(Scissors s);
Outcome eval(Rock r);
}
class Paper implements Item {
//compete方法解决调用方法的动态绑定,实现第一次分发。内部再调用eval方法,
//通过参数的重载实现第二次分发
/*
方法重载是静态分派(多发)(java中静态绑定),方法重写是动态分派(单发)(java中通过动态绑定实现)。
如果想要实单发,则compete(par)调用时,par必须是实际类型,将compete重载为三个函数。且同时要求nextitem重写为3个函数,以产生明确的导出类类型。
a.compete(b)
a是item,b是item。这样提供一个item接口,避免重载上述compete和nextItem方法。
先根据a的实际类型选择具体compete方法(动态)。 然后调用eval(b),(eval重载为三个导出类类型),在调用eval的时候就必须根据b的具体类型来调用(静态)。
*/
public Outcome compete(Item it){return it.eval(this);}
public Outcome eval(Paper p){return DRAW;}
public Outcome eval(Scissors s){return WIN;}
public Outcome eval(Rock r){return LOSE;}
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 WIN;}
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 RoShaBo1 {
static final int SIZE = 5;
private static Random rand = new Random(47);
public static Item nextItem(){
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(nextItem(),nextItem());
}
}
/*Rock vs. Rock: DRAW
Paper vs. Rock: WIN
Paper vs. Rock: WIN
Paper vs. Rock: WIN
Scissors vs. Paper: WIN*/
19.11.1 使用enum分发
““java
package Ch19;
import static Ch19.Outcome.*;
public enum RoshamBo2
implements Competitor
{
PAPER(DRAW,LOSE, WIN), SCISSORS(LOSE,WIN,DRAW), ROCK(LOSE,WIN,DRAW);
private Outcome vPAPER;
private Outcome vSCISSORS;
private Outcome vROCK;
RoshamBo2(Outcome paper, Outcome scissors, Outcome rock){
this.vSCISSORS = scissors;
this.vPAPER = paper;
this.vROCK = rock;
}
public Outcome compete(RoshamBo2 it)
{
switch (it) {
default:
case PAPER:
return this.vROCK;
case SCISSORS:
return this.vPAPER;
case ROCK:
return this.vSCISSORS;
}
}
public static void main(String[] args)
{
RoShamBo.play(RoshamBo2.class, 5);
}
}
/*ROCK vs. ROCK: WIN
SCISSORS vs. ROCK: WIN
SCISSORS vs. ROCK: WIN
SCISSORS vs. ROCK: WIN
PAPER vs. SCISSORS: DRAW
*/
““
19.11.3 使用EnumMap分发
package Ch19;
import java.util.EnumMap;
import static Ch19.Outcome.*;
public enum RoShamBo5 implements Competitor<RoShamBo5>{
PAPER,SCISSORS,ROCK;
//EnumMap中定义了final static keyType变量,必须在构造器中初始化。
//所以有RoShamBo.class
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,5);
}
}//本质也是多维数组
/*ROCK vs. ROCK: DRAW
SCISSORS vs. ROCK: LOSE
SCISSORS vs. ROCK: LOSE
SCISSORS vs. ROCK: LOSE
PAPER vs. SCISSORS: LOSE*/
19.11.4 使用二维数组
数组不安全。