美文网首页
Java编程思想(十八) 枚举类型

Java编程思想(十八) 枚举类型

作者: kaiker | 来源:发表于2022-07-02 15:55 被阅读0次

    基本enum特性

    • 创建enum时,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum
    • ordinal()方法返回一个int值,这是每个enum实例在声明时的次序,从0开始。
    • 在enum实例上调用getDeclaringClass()方法,我们就能知道其所属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);
        }
      }
    } /* 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
    *///:~
    

    2、向enum中添加新方法

    • 除了不能继承自一个enum之外,基本上可以将enum看成一个常规类。
    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());
      }
    } /* Output:
    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
    *///:~
    

    3、switch语句中的enum

    • 一般来说,switch中智能使用整数值,而枚举实例天生就具备整数值的次序,并且可以通过ordinal()方法取得其次序。
    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类都继承自Enum类。但是Enum类没有values()方法。
    • values()是有编译器添加的static方法。
    • 由于values()方法是有编译器擦擦入到enum定义中的static方法,所以如果转型成Enum就访问不到values()了,但是Class里有一个getEnumConstants()方法可以获得。
    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);
      }
    } /* Output:
    HITHER
    YON
    *///:~
    

    5、实现而非继承

    • enum不能再继承其他类。

    6、使用接口组织枚举

    • 使用子类将一个enum中的元素进行分组、希望扩展原enum中的元素。
    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;
      }
    } ///:~
    
    

    7、EnumSet

    • 将一个long值作为比特向量,所以EnumSet非常快速高效。
    • EnumSet中的元素必须来自一个enum。
    public class EnumSets {
      public static void main(String[] args) {
        EnumSet<AlarmPoints> points =
          EnumSet.noneOf(AlarmPoints.class); // Empty set
        points.add(BATHROOM);
        print(points);
        points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
        print(points);
        points = EnumSet.allOf(AlarmPoints.class);
        points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
        print(points);
        points.removeAll(EnumSet.range(OFFICE1, OFFICE4));
        print(points);
        points = EnumSet.complementOf(points);
        print(points);
      }
    } /* Output:
    [BATHROOM]
    [STAIR1, STAIR2, BATHROOM, KITCHEN]
    [LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY]
    [LOBBY, BATHROOM, UTILITY]
    [STAIR1, STAIR2, OFFICE1, OFFICE2, OFFICE3, OFFICE4, KITCHEN]
    *///:~
    

    8、EnumMap

    • Key必须来自一个enum。EnumMap在内部可由数组实现。
    public class EnumMaps {public class EnumSets {
      public static void main(String[] args) {
        EnumSet<AlarmPoints> points =
          EnumSet.noneOf(AlarmPoints.class); // Empty set
        points.add(BATHROOM);
        print(points);
        points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
        print(points);
        points = EnumSet.allOf(AlarmPoints.class);
        points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
        print(points);
        points.removeAll(EnumSet.range(OFFICE1, OFFICE4));
        print(points);
        points = EnumSet.complementOf(points);
        print(points);
      }
    } /* Output:
    [BATHROOM]
    [STAIR1, STAIR2, BATHROOM, KITCHEN]
    [LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY]
    [LOBBY, BATHROOM, UTILITY]
    [STAIR1, STAIR2, OFFICE1, OFFICE2, OFFICE3, OFFICE4, KITCHEN]
    *///:~
      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!"); }
        });
        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 { // If there's no value for a particular key:
          em.get(UTILITY).action();
        } catch(Exception e) {
          print(e);
        }
      }
    } /* Output:
    BATHROOM: Bathroom alert!
    KITCHEN: Kitchen fire!
    java.lang.NullPointerException
    *///:~
    

    9、常量相关方法

    • 可以为enum实例编写方法,从而为每个enum实例赋予各自不同的行为。
    • 要实现常量相关的方法,需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象方法。
    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());
      }
    } /* (Execute to see output) *///:~
    

    使用enum的职责链

    使用enum

    public enum Input {
      NICKEL(5), DIME(10), QUARTER(25), DOLLAR(100),
      TOOTHPASTE(200), CHIPS(75), SODA(100), SOAP(50),
      ABORT_TRANSACTION {
        public int amount() { // Disallow
          throw new RuntimeException("ABORT.amount()");
        }
      },
      STOP { // This must be the last instance.
        public int amount() { // Disallow
          throw new RuntimeException("SHUT_DOWN.amount()");
        }
      };    
      int value; // In cents
      Input(int value) { this.value = value; }
      Input() {}
      int amount() { return value; }; // In cents
      static Random rand = new Random(47);
      public static Input randomSelection() {
        // Don't include STOP:
        return values()[rand.nextInt(values().length - 1)];
      }
    } ///:~
    
    enum Category {
      MONEY(NICKEL, DIME, QUARTER, DOLLAR),
      ITEM_SELECTION(TOOTHPASTE, CHIPS, SODA, SOAP),
      QUIT_TRANSACTION(ABORT_TRANSACTION),
      SHUT_DOWN(STOP);
      private Input[] values;
      Category(Input... types) { values = types; }  
      private static EnumMap<Input,Category> categories =
        new EnumMap<Input,Category>(Input.class);
      static {
        for(Category c : Category.class.getEnumConstants())
          for(Input type : c.values)
            categories.put(type, c);
      }
      public static Category categorize(Input input) {
        return categories.get(input);
      }
    }   
    
    public class VendingMachine {
      private static State state = State.RESTING;
      private static int amount = 0;
      private static Input selection = null;
      enum StateDuration { TRANSIENT } // Tagging enum
      enum State {
        RESTING {
          void next(Input input) {
            switch(Category.categorize(input)) {
              case MONEY:
                amount += input.amount();
                state = ADDING_MONEY;
                break;
              case SHUT_DOWN:
                state = TERMINAL;
              default:
            }
          }
        },  
        ADDING_MONEY {
          void next(Input input) {
            switch(Category.categorize(input)) {
              case MONEY:
                amount += input.amount();
                break;
              case ITEM_SELECTION:
                selection = input;
                if(amount < selection.amount())
                  print("Insufficient money for " + selection);
                else state = DISPENSING;
                break;
              case QUIT_TRANSACTION:
                state = GIVING_CHANGE;
                break;
              case SHUT_DOWN:
                state = TERMINAL;
              default:
            }
          }
        },  
        DISPENSING(StateDuration.TRANSIENT) {
          void next() {
            print("here is your " + selection);
            amount -= selection.amount();
            state = GIVING_CHANGE;
          }
        },
        GIVING_CHANGE(StateDuration.TRANSIENT) {
          void next() {
            if(amount > 0) {
              print("Your change: " + amount);
              amount = 0;
            }
            state = RESTING;
          }
        },  
        TERMINAL { void output() { print("Halted"); } };
        private boolean isTransient = false;
        State() {}
        State(StateDuration trans) { isTransient = true; }
        void next(Input input) {
          throw new RuntimeException("Only call " +
            "next(Input input) for non-transient states");
        }
        void next() {
          throw new RuntimeException("Only call next() for " +
            "StateDuration.TRANSIENT states");
        }
        void output() { print(amount); }
      } 
      static void run(Generator<Input> gen) {
        while(state != State.TERMINAL) {
          state.next(gen.next());
          while(state.isTransient)
            state.next();
          state.output();
        }
      }
      public static void main(String[] args) {
        Generator<Input> gen = new RandomInputGenerator();
        if(args.length == 1)
          gen = new FileInputGenerator(args[0]);
        run(gen);
      }
    }   
    
    // For a basic sanity check:
    class RandomInputGenerator implements Generator<Input> {
      public Input next() { return Input.randomSelection(); }
    }
    
    // Create Inputs from a file of ';'-separated strings:
    class FileInputGenerator implements Generator<Input> {
      private Iterator<String> input;
      public FileInputGenerator(String fileName) {
        input = new TextFile(fileName, ";").iterator();
      }
      public Input next() {
        if(!input.hasNext())
          return null;
        return Enum.valueOf(Input.class, input.next().trim());
      }
    } /* Output:
    25
    50
    75
    here is your CHIPS
    0
    100
    200
    here is your TOOTHPASTE
    0
    25
    35
    Your change: 35
    0
    25
    35
    Insufficient money for SODA
    35
    60
    70
    75
    Insufficient money for SODA
    75
    Your change: 75
    0
    Halted
    *///:~
    

    10、多路分发

    enum分发

    ublic 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);
      }
    } /* Output:
    ROCK vs. ROCK: DRAW
    SCISSORS vs. ROCK: LOSE
    SCISSORS vs. ROCK: LOSE
    SCISSORS vs. ROCK: LOSE
    PAPER vs. SCISSORS: LOSE
    PAPER vs. PAPER: DRAW
    PAPER vs. SCISSORS: LOSE
    ROCK vs. SCISSORS: WIN
    SCISSORS vs. SCISSORS: DRAW
    ROCK vs. SCISSORS: WIN
    SCISSORS vs. PAPER: WIN
    SCISSORS vs. PAPER: WIN
    ROCK vs. PAPER: LOSE
    ROCK vs. SCISSORS: WIN
    SCISSORS vs. ROCK: LOSE
    PAPER vs. SCISSORS: LOSE
    SCISSORS vs. PAPER: WIN
    SCISSORS vs. PAPER: WIN
    SCISSORS vs. PAPER: WIN
    SCISSORS vs. PAPER: WIN
    *///:~
    

    常量分发

    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:
            case PAPER: return WIN;
            case SCISSORS: return DRAW;
            case ROCK: return LOSE;
          }
        }
      },
      ROCK {
        public Outcome compete(RoShamBo3 it) {
          switch(it) {
            default:
            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);
      }
    } /* Same output as RoShamBo2.java *///:~
    

    EnumMap

    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);
      }
    } /* Same output as RoShamBo2.java *///:~
    

    二维数组

    enum RoShamBo6 implements Competitor<RoShamBo6> {
      PAPER, SCISSORS, ROCK;
      private static Outcome[][] table = {
        { DRAW, LOSE, WIN }, // PAPER
        { WIN, DRAW, LOSE }, // SCISSORS
        { LOSE, WIN, DRAW }, // ROCK
      };
      public Outcome compete(RoShamBo6 other) {
        return table[this.ordinal()][other.ordinal()];
      }
      public static void main(String[] args) {
        RoShamBo.play(RoShamBo6.class, 20);
      }
    } ///:~
    

    相关文章

      网友评论

          本文标题:Java编程思想(十八) 枚举类型

          本文链接:https://www.haomeiwen.com/subject/rfkkbrtx.html