一、实现,而非继承

我们已经知道,所有的enum都继承自java.lang.Enum类。由于java不支持多重继承,所以你的enum不能再继承其他类:

enum NotPossible extends Pet{ }

然而,在我们创建一个新的enum时,可以同时实现一个或多个接口:

package enumerated.cartoons;import java.util.Random;import com.buba.util.Generator;enum CartoonCharacter implements Generator<CartoonCharacter> {SLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB;Random r = new Random();@Overridepublic CartoonCharacter next() {return values()[r.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);}}
}

这个结果有点奇怪,不过你必须要有一个enum实例才能调用其上的方法。现在,在任何接受Generator参数的方法中,例如printNext(),都可以使用CartoonCharacter。

二、随机选取

就像你在CartoonCharacter.next()中看到的那样,本章中的很多示例都需要从enum实例中进行随机选择。我们可以利用泛型,从而使得这个工作更一般化,并将其加入到我们的工具库中。

package com.buba.util;import java.util.Random;public class Enums {private static Random r = new Random();public static <T extends Enum<T>> T random(Class<T> ec) {return random(ec.getEnumConstants());}public static <T> T random(T[] values) {return values[r.nextInt(values.length)];}
}

古怪的语法<T extends Enum<T>>表示T是一个enum实例。而将Class<T>作为参数的话,我们就可以利用Class对象得到enum实例的数组了。重载后,random()方法只需使用T[]作为参数,因为它并不会调用Enum上的任何操作,它只需从数组中随机选择一个元素即可。这样,最终返回类型正是enum的类型。

下面是random()方法的一个简单示例:

package enumerated;import com.buba.util.Enums;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 < 20; i++) {System.out.print(Enums.random(Activity.class) + " ");}}
}

虽然Enum只是一个相当短小的类,但是在本章中你会发现,它能消除很多重复的代码。重复总会制造麻烦,因此消除重复总是有益处的。

三、使用接口组织枚举

无法从enum继承子类有时很令人沮丧。这种需求有时源自我们希望扩展原enum中的元素,有时是因为我们希望使用子类将一个enum中的元素进行分组。

在一个接口的内部,创建实现该接口的枚举,以此将元素进行分组,可以达到将枚举元素分类组织的目的。举例来说,假设你想用enum来表示不同类别的食物,同时还希望每个enum元素仍然保持Food类型。那可以这样实现:

package enumerated.menu;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而言,实现接口是使其子类化的唯一办法,所以嵌入在Food中的每个enum都实现了Food接口。现在,在下面的程序中,我们可以说“所有东西都是某种类型的Food”:

package enumerated.menu;import enumerated.menu.Food.Appetizer;
import enumerated.menu.Food.Coffee;
import enumerated.menu.Food.Dessert;
import enumerated.menu.Food.MainCourse;public class TypeOfFood {public static void main(String[] args) {Food food = Appetizer.SALAD;food = MainCourse.LASAGNE;food = Dessert.GELATO;food = Coffee.CAPPUCCINO;}
}

如果enum类型实现了Food接口,那么我们就可以将其实例向上转型为Food,所以上例中的所有东西都是Food。

然而,当你需要与一大堆类型打交道时,接口就不如enum好用了。例如,如果你想创建一个“枚举的枚举”,那么可以创建一个新的enum,然后用其实例包装Food中的每一个enum类:

package enumerated.menu;import com.buba.util.Enums;public enum Course {APPETIZER(Food.Appetizer.class), MAINCOURSE(Food.MainCourse.class), DESSERT(Food.Dessert.class),COFFEE(Food.Coffee.class);private Food[] values;private Course(Class<? extends Food> kind) {values = kind.getEnumConstants();}public Food randomSelection() {return Enums.random(values);}
}

在上面的程序中,每一个Course的实例都将其对应的Class对象作为构造器的参数。通过getEnumConstants()方法,可以从该Class对象中取得某个Food子类的所有enum实例。这些实例在randomSelection()中被用到。因此,通过从每一个Course实例中随机地选择一个Food,我们便能够生成一份菜单:

package enumerated.menu;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("-----------");}}
}

在这里例子中,我们通过遍历每一个Course实例来获得“枚举的枚举”的值。稍后,在VendingMachine.java中,我们看到另一种组织枚举实例的方式,但其也有一些其他的限制。

此外,还有一种更简洁的管理枚举的办法,就是将一个enum嵌套在另一个enum内。就像这样:

package enumerated;import com.buba.util.Enums;public enum SecurityCategory {STOCK(Security.Stock.class), BOND(Security.Bond.class);Security[] values;SecurityCategory(Class<? extends Security> kind) {values = kind.getEnumConstants();}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());}}
}

Security接口的作用是将其所包含的enum组合成一个公共类型,这一点是有必要的。然后SecurityCategory才能将Security才能将Security中的enum作为其构造器的参数使用,以起到组织的效果。

如果我们将这种方式应用于Food的例子,结果应该这样:

package enumerated.menu;import com.buba.util.Enums;public enum Meal2 {APPETIZER(Food.Appetizer.class), MAINCOURSE(Food.MainCourse.class), DESSERT(Food.Dessert.class),COFFEE(Food.Coffee.class);private Food[] values;private Meal2(Class<? extends Food> kind) {values = kind.getEnumConstants();}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;}}public Food randomSelection() {return Enums.random(values);}public static void main(String[] args) {for (int i = 0; i < 5; i++) {for (Meal2 meal : Meal2.values()) {Food food = meal.randomSelection();System.out.println(food);}System.out.println("-------------");}}
}

其实,这仅仅是重新组织了一下代码,不过多数情况下,这种方式使你的代码具有更清晰的结构。

如果本文对您有很大的帮助,还请点赞关注一下。

枚举类型(2):实现,而非继承、随机选取、使用接口组织枚举相关推荐

  1. java习题——集合类、枚举类型与泛型——英文字母输出,掷骰子,彩虹枚举,体检记录模拟

    1.使用数组和ArrayList类,先输出A→Z,再输出z→a import java.util.*;public class CharacterPrinter {public static void ...

  2. thinking-in-java(19)枚举类型

    [0]开场白 1)关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用: 2)所有的枚举类都继承自 Enum,通过 enumClass.getSu ...

  3. 《Java 编程思想》--第十九章:枚举类型

    关键字enum可以将一组具名的值得优先级和创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用 enum的定义和使用方式方式: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...

  4. java基础(十一) 枚举类型

    枚举类型Enum的简介 1.什么是枚举类型 枚举类型: 就是由一组具有名的值的有限集合组成新的类型.(即新的类). 好像还是不懂,别急,咱们先来看一下 为什么要引入枚举类型 在没有引入枚举类型前,当我 ...

  5. java基础篇(11) 枚举类型

    枚举类型Enum的简介 1.什么是枚举类型 枚举类型: 就是由一组具有名的值的有限集合组成新的类型.(即新的类). 好像还是不懂,别急,咱们先来看一下 为什么要引入枚举类型 在没有引入枚举类型前,当我 ...

  6. java枚举类型季节实例_Java之枚举类

    目录 一.为何引入枚举类型(为了替代魔法值) 什么是魔法值?魔法值有哪些隐患,见另一篇文章编码规约之使用Enum枚举类替代魔法值 那么为什么不用静态变量来替换魔法值呢? 有时候,变量的取值只在一个有限 ...

  7. C++ 学习笔记(19)new/delete表达式、定位new、typeid、dynamic_cast、type_info、枚举类型、成员函数指针、union、位域、volatile限定符、链接指示

    C++ 学习笔记(19)new/delete表达式.定位new.typeid.dynamic_cast.type_info.枚举类型.成员函数指针.union.位域.volatile限定符.链接指示 ...

  8. Java枚举类型的使用

    1. 在J2SE5.0中要定义枚举类型是使用enum关键词,枚举类型主要提供一些常数.如下列代码定义了Action枚举类型: 1. 在J2SE5.0中要定义枚举类型是使用enum关键词,枚举类型主要提 ...

  9. 谈Java语言规范之枚举类型

    文章目录 枚举类型 一. 枚举常量 二.枚举主体声明 对枚举常数自我引用的限制: 三.枚举成员 这不是一顿快餐,希望你沉淀下来,细细品尝 写在前面 枚举类型可以考虑用来替换接口中的常量声明.并且 &l ...

最新文章

  1. 使用pytesseract出现错误:“[WinError 2] 系统找不到指定的文件
  2. html与css知识总结,html和css知识总结(示例代码)
  3. mysql 内存监控_mysql cpu和内存监控
  4. 为什么C++(来自C++之父的观点)
  5. Java黑皮书课后题第7章:7.14(计算gcd)编写方法,返回个数不确定的整数的最大公约数。编写一个测试程序,提示用户输入5个数字,调用该方法找出这些数的最大公约数,并显示这个最大公约数
  6. java自动转换_java类型转换详解(自动转换和强制转换)
  7. 电路板上的插头怎么拔下来_空调插头一直不拔费电吗?实测一周竟然发现了真相!...
  8. Tensorboard可视化:基于LeNet5进行面部表情分类
  9. Linux系统IO端口,Linux系统对IO端口和IO内存的管理
  10. vue组件在ios不渲染_VueJS:点击后渲染新组件
  11. JS之模板技术(aui / artTemplate)
  12. CodeForces - 982C Cut 'em all!
  13. POJ 1182 食物链(带权并查集)
  14. Oracle密码过期改密失败
  15. Unity ToLua LuaFramework_UGUI学习笔记
  16. 16. M601 低功耗测试
  17. 建筑企业收并购系列二:吸收合并政策影响
  18. TI-Davinci开发系列之七DVSDK-4.03目录介绍
  19. 复杂的1秒 图解Google搜索技术
  20. 【Android】腾讯即时通讯SDK的初次接入的详细记录

热门文章

  1. 递归函数与内置函数和函数式编程
  2. linux 找回网卡的uuid_Linux系统怎么查看网卡的UUID
  3. 计算机考试qq用户找回密码,找回QQ密码_详细讲述QQ密码找回教程方法【图文】-太平洋电脑网PConline-太平洋电脑网...
  4. 【黑金ZYNQ7000系列原创视频教程】07.自定义IP——定制RTC IP实验
  5. Unity 屏幕特效后期处理 OnRenderImage
  6. 世界主要国家人工智能战略简析
  7. web前端-javascript常用的document对象使用详解
  8. 【运维】浪潮服务器一块硬盘显示红色Offline(或者Failed)解决办法
  9. 原来,《心灵奇旅》竟是用VR制作的
  10. 地铁大数据挖掘之数据预处理——从原始一卡通数据提取城市地铁客流(二)