关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用

基本enum特性

创建enum时,编译器会自动的生成一个相关的类,这个类继承与java.lang.Enum,(隐式的)

  1. enum Shrubbery{
  2. GROUND,CRAWLING,HANGING
  3. }
  4. public class EnumClass {
  5. public static void main(String[] args) {
  6. for (Shrubbery s : Shrubbery.values()) {
  7. System.out.println(s + " ordinal " + s.ordinal());//ordinal类似于下标
  8. System.out.println(s.compareTo(Shrubbery.CRAWLING) + " ");
  9. System.out.println(s.equals(Shrubbery.CRAWLING) + " ");
  10. System.out.println(s == Shrubbery.CRAWLING);
  11. System.out.println(s.getDeclaringClass());
  12. System.out.println(s.name());
  13. System.out.println("==================");
  14. }
  15. for (String s : "HANGING CRAWLING GROUND".split(" ")) {
  16. Shrubbery shrubbery = Enum.valueOf(Shrubbery.class,s);//返回具有指定名称的指定enum常量
  17. System.out.println(shrubbery);
  18. }
  19. }
  20. }

向enum中添加新方法

enum可以看成一个常规的类,也可以实现接口,可以向其中添加新的方法,设置可以有main方法

  1. public enum OzWitch {
  2. WEST("Miss Gulch, aka the Wicked Witch of the West"),
  3. NORTH("Glinda, the Good Witch of the North"),
  4. EAST("Wicked Witch of the East, wearer of the Ruby " +
  5. "Slippers, crushed by Dorothy's house"),
  6. SOUTH("Good by inference, but missing");//必须要在实例最后加上分号
  7. //必须先定义enum实例,不能再没有定义实例之前定义任何方法或者属性
  8. private String description;
  9. //只能在内部使用器构造器构建enum实例,构建结束后就不能使用构造器构造任何实例了.
  10. private OzWitch(String description) {
  11. this.description = description;
  12. }
  13. public String getDescription() { return description; }
  14. public static void main(String[] args) {
  15. for(OzWitch witch : OzWitch.values())
  16. print(witch + ": " + witch.getDescription());
  17. }
  18. }

覆盖enum方法

覆盖enum的tostring方法和一般的类没有区别

swith语句中的enum

  1. enum Signal{
  2. GREEN,YELLOW,RED
  3. }
  4. public class TrafficLight {
  5. Signal color = Signal.RED;
  6. public void change(){
  7. switch (color){
  8. case RED:
  9. color = Signal.GREEN;
  10. break;
  11. case GREEN:
  12. color = Signal.YELLOW;
  13. break;
  14. case YELLOW:
  15. color = Signal.RED;
  16. break;
  17. }
  18. }
  19. @Override
  20. public String toString() {
  21. return "TrafficLight{" +
  22. "color=" + color +
  23. '}';
  24. }
  25. public static void main(String[] args) {
  26. final TrafficLight trafficLight = new TrafficLight();
  27. for (int i = 0; i < 7; i++) {
  28. System.out.println(trafficLight);
  29. trafficLight.change();
  30. }
  31. }
  32. }

Values的神秘之处

创建的enum类都继承于Enum类,但是Enum类中并没有values方法

  1. enum Explore{HERE,THERE}
  2. public class Reflection {
  3. public static Set<String> analyze(Class<?> enumClass){
  4. System.out.println("---Analyzing " + enumClass + "---");
  5. System.out.println("Interface: ");
  6. for (Type t : enumClass.getGenericInterfaces()) {
  7. System.out.println(t);
  8. }
  9. System.out.println("Base : " + enumClass.getSuperclass());
  10. System.out.println("Method: ");
  11. Set<String> methods = new TreeSet<>();
  12. for (Method method : enumClass.getMethods()) {
  13. methods.add(method.getName());
  14. }
  15. System.out.println(methods);
  16. return methods;
  17. }
  18. public static void main(String[] args) {
  19. Set<String> exploreMethods = analyze(Explore.class);
  20. Set<String> enumMethods = analyze(Enum.class);
  21. System.out.println("Explore.containAll(Enum) ? " + exploreMethods.containsAll(enumMethods));
  22. System.out.print("Explore.removeAll(Enum): ");
  23. exploreMethods.removeAll(enumMethods);
  24. System.out.println(exploreMethods);
  25. }
  26. }

values是由编辑器添加的static方法
Class中有一个getEnumConstants()方法,即便Enum中没有values方法,我们仍然可以通过Class对象取得所有enum实例

  1. enum Search{
  2. HITHER,YON
  3. }
  4. public class UpCastEnum {
  5. public static void main(String[] args) {
  6. Search[] vals = Search.values();
  7. Enum e = Search.HITHER;
  8. for (Enum enumConstant : e.getClass().getEnumConstants()) {
  9. System.out.println(enumConstant);
  10. }
  11. }
  12. }

getEnumConstants()方法是Class上的方法,也可以对不是枚举的类型调用此方法

  1. public class NonEnum {
  2. public static void main(String[] args) {
  3. Class<Integer> intClass = int.class;
  4. for (Object enumConstant : intClass.getEnumConstants()) {
  5. System.out.println(enumConstant);
  6. }
  7. }
  8. }

但是结果会显示空指针

实现而非继承

enum隐式的继承了Enum,所以不能继承其他的类,但是可以实现接口

  1. enum CartoonCharacter implements Generator<CartoonCharacter> {
  2. SLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB;
  3. private Random rand = new Random(47);
  4. public CartoonCharacter next() {
  5. return values()[rand.nextInt(values().length)];
  6. }
  7. }
  8. public class EnumImplementation {
  9. public static <T> void printNext(Generator<T> rg) {
  10. System.out.print(rg.next() + ", ");
  11. }
  12. public static void main(String[] args) {
  13. // Choose any instance:
  14. CartoonCharacter cc = CartoonCharacter.BOB;//拿到了一个实例就遍历出枚举中其他的实例
  15. for (int i = 0; i < 10; i++)
  16. printNext(cc);
  17. }
  18. }

随机选取

  1. enum Activity { SITTING, LYING, STANDING, HOPPING,
  2. RUNNING, DODGING, JUMPING, FALLING, FLYING }
  3. public class RandomTest {
  4. public static void main(String[] args) {
  5. for(int i = 0; i < 20; i++)
  6. System.out.print(Enums.random(Activity.class) + " ");
  7. }
  8. }

使用接口组织枚举

在一个接口的内部创建该接口的枚举,以此将元素分组,可以达到将枚举元素分组的目的

  1. public interface Food {
  2. enum Appetizer implements Food {
  3. SALAD, SOUP, SPRING_ROLLS;
  4. }
  5. enum MainCourse implements Food {
  6. LASAGNE, BURRITO, PAD_THAI,
  7. LENTILS, HUMMOUS, VINDALOO;
  8. }
  9. enum Dessert implements Food {
  10. TIRAMISU, GELATO, BLACK_FOREST_CAKE,
  11. FRUIT, CREME_CARAMEL;
  12. }
  13. enum Coffee implements Food {
  14. BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
  15. LATTE, CAPPUCCINO, TEA, HERB_TEA;
  16. }
  17. }

对于enum来说实现接口时使其子类化的唯一办法,所以,Food中的每个enum都实现了Food接口,而且可以向上转型为Food
如果要和一堆类型打交道,接口就没有enum好用了,需要先创建一个enum,然后用它的实例包装Food中的每一个enum类

  1. public enum Course {
  2. APPETIZER(Food.Appetizer.class),
  3. MAINCOURSE(Food.MainCourse.class),
  4. DESSERT(Food.Dessert.class),
  5. COFFEE(Food.Coffee.class);
  6. private Food[] values;
  7. private Course(Class<? extends Food> kind) {
  8. values = kind.getEnumConstants(); //可以从该class对象中获得某个Food子类中的
  9. //所有enum实例
  10. }
  11. public Food randomSelection() {
  12. return Enums.random(values);//随机在菜单中产生菜品
  13. }
  14. }
  1. public class Meal {
  2. public static void main(String[] args) {
  3. for (int i = 0; i < 5; i++) {
  4. for (Course course : Course.values()) {
  5. Food food = course.randomSelection();
  6. System.out.println(food);
  7. }
  8. System.out.println("---");
  9. }
  10. }
  11. }

还有一种更简洁的处理方式,就是将一个enum嵌套在另一个enum中

  1. enum SecurityCategory {
  2. STOCK(Security.Stock.class), BOND(Security.Bond.class);
  3. Security[] values;
  4. SecurityCategory(Class<? extends Security> kind) {
  5. //将Security中enum参数作为它的构造器参数
  6. values = kind.getEnumConstants();
  7. }
  8. interface Security {//将其包含的enum组合成一个公共类型
  9. enum Stock implements Security {SHORT, LONG, MARGIN}
  10. enum Bond implements Security {MUNICIPAL, JUNK}
  11. }
  12. public Security randomSelection() {
  13. return Enums.random(values);
  14. }
  15. public static void main(String[] args) {
  16. for (int i = 0; i < 10; i++) {
  17. SecurityCategory category =
  18. Enums.random(SecurityCategory.class);
  19. System.out.println(category + ": " +
  20. category.randomSelection());
  21. }
  22. }
  23. }

使用EnumSet替代标志
EnumSet的设计充分考虑到了速度因素,相比于HashSet而言,非常的快
使用EnumSet的优点:在说明有一个二进制是否存在的时候有更好的表达能力

  1. public enum AlarmPoints {
  2. STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3,
  3. OFFICE4, BATHROOM, UTILITY, KITCHEN
  4. }
  1. public class EnumSets {
  2. public static void main(String[] args) {
  3. EnumSet<AlarmPoints> points =
  4. EnumSet.noneOf(AlarmPoints.class); //返回一个枚举类型的空集合
  5. points.add(BATHROOM);//添加元素
  6. print(points);
  7. points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));//添加枚举中的元素
  8. print(points);
  9. points = EnumSet.allOf(AlarmPoints.class);//添加枚举中所有的元素
  10. points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
  11. print(points);
  12. points.removeAll(EnumSet.range(OFFICE1, OFFICE4));//range创建一个enum集,左右都是闭区间,然后再删除
  13. print(points);
  14. points = EnumSet.complementOf(points);//添加补集元素
  15. print(points);
  16. }
  17. }

使用EnumMap

是一种特殊的Map,它要求其中的键必须是一个enum,由于enum本身的限制,所以EnumMap在内部可以由数组实现,只能将enum的实例作为键来调用put()方法

命令设计模式:

命令模式只需要一个只有单一方法的接口,然后从该接口实现具有各自不同行为的多个子类

  1. public class EnumMaps {
  2. public static void main(String[] args) {
  3. EnumMap<AlarmPoints, Command> em = new EnumMap<>(AlarmPoints.class);
  4. em.put(KITCHEN, new Command() { //使用的匿名内部类
  5. @Override
  6. public void action() {
  7. System.out.println("Kitchen fire");
  8. }
  9. });
  10. em.put(BATHROOM, new Command() {
  11. public void action() {
  12. print("Bathroom alert!");
  13. }
  14. });
  15. for (Map.Entry<AlarmPoints, Command> e : em.entrySet()) {//循环遍历键值对
  16. printnb(e.getKey() + ": ");
  17. e.getValue().action();
  18. }
  19. try {
  20. em.get(UTILITY).action();//没有重写方法的会报空指针
  21. } catch (Exception e) {
  22. print(e);
  23. }
  24. }
  25. }

常量相关的方法

允许为每个enum实例编写方法,从而为没有enum实例赋予不同的行为

  1. public enum ConstantSpecificMethod {
  2. DATE_TIME {
  3. String getInfo() {//每个实例都赋予不同的行为
  4. return
  5. DateFormat.getDateInstance().format(new Date());//获取日期,并且指定了格式为format
  6. }
  7. },
  8. CLASSPATH {
  9. String getInfo() {
  10. return System.getenv("CLASSPATH");
  11. }//获取指定环境变量的值
  12. },
  13. VERSION {
  14. String getInfo() {
  15. return System.getProperty("java.version");//获取由指定键指示的值
  16. }
  17. };
  18. abstract String getInfo();//每个实例都可以对其进行重写
  19. public static void main(String[] args) {
  20. for (ConstantSpecificMethod csm : values())
  21. System.out.println(csm.getInfo());
  22. }
  23. }
  1. public class CarWash {
  2. public enum Cycle {
  3. UNDERBODY {
  4. void action() {
  5. print("Spraying the underbody");
  6. }
  7. },
  8. WHEELWASH {
  9. void action() {
  10. print("Washing the wheels");
  11. }
  12. },
  13. PREWASH {
  14. void action() {
  15. print("Loosening the dirt");
  16. }
  17. },
  18. BASIC {
  19. void action() {
  20. print("The basic wash");
  21. }
  22. },
  23. HOTWAX {
  24. void action() {
  25. print("Applying hot wax");
  26. }
  27. },
  28. RINSE {
  29. void action() {
  30. print("Rinsing");
  31. }
  32. },
  33. BLOWDRY {
  34. void action() {
  35. print("Blowing dry");
  36. }
  37. };
  38. abstract void action();
  39. }
  40. EnumSet<Cycle> cycles =
  41. EnumSet.of(Cycle.BASIC, Cycle.RINSE); //相当于基本套餐
  42. public void add(Cycle cycle) {
  43. cycles.add(cycle);
  44. }
  45. public void washCar() {
  46. for (Cycle c : cycles)
  47. c.action();
  48. }
  49. public String toString() {
  50. return cycles.toString();
  51. }
  52. public static void main(String[] args) {
  53. CarWash wash = new CarWash();
  54. print(wash);
  55. wash.washCar();//基本套餐
  56. wash.add(Cycle.BLOWDRY);
  57. wash.add(Cycle.BLOWDRY);//增值的服务
  58. wash.add(Cycle.RINSE);
  59. wash.add(Cycle.HOTWAX);
  60. print(wash);
  61. wash.washCar();
  62. }
  63. }