Java編程思想 Ch19 枚舉

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 使用二維數組

數組不安全。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章