本文转载自博客 - Java枚举类型, 博主对原文内容及结构作了一定的修改.
1 枚举类的编译特性
从JDK 5开始, Java中多了一个关键字——enum
: 可以将一组具有名称的值(包括String、Integer等)的有限集合创建为一种新的类型, 而这些具名的值可以作为常规的程序组件使用.
这些具名的值称为枚举值, 这种新的类型称为枚举类型.
下面是一个简单的表示星期几的枚举:
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
创建enum时, 编译器会自动为enum类添加一些特性, 比如:
a) 创建
toString()
方法: 以便显示某个枚举实例的名字;b) 创建
name()
方法: 获取枚举类型中某个实例的名称;c) 创建
ordinal()
方法: 表示某个特定枚举常量的申明顺序, 从0开始;d) 创建
values()
方法: 按照枚举常量的申明顺序产生这些常量构成的数组…...
关于这些特性, 编写测试方法如下:
public static void main(String[] args) {
// 获取枚举类型的父类型
System.out.println(Day.class.getSuperclass());
// 遍历枚举类型中的值
for (Day day : Day.values()) {
System.out.println(day.name() + ", ordinal: " + day.ordinal());
}
}
程序的输出结果是:
class java.lang.Enum // 所有enum的父类都是它
SUNDAY, ordinal: 0 // ordinal()方法从0开始计数
MONDAY, ordinal: 1
TUESDAY, ordinal: 2
WEDNESDAY, ordinal: 3
THURSDAY, ordinal: 4
FRIDAY, ordinal: 5
SATURDAY, ordinal: 6
从输出结果可以看到: 枚举类型Day的父类是java.lang.Enum
——我们的代码中并没有指明extends
动作, 所以这是由编译器完成的.
由于枚举实例已经继承了java.lang.Enum
, 而Java又不支持多继承, 所以enum
不能再继承其他的类, 但是能实现接口.
==> 所以, enum
只是看起来像一种新的数据类型, 除了上面讲到的这些特殊的编译行为, 并没有什么特殊的地方.
2 向枚举类中添加方法
除了不能继承一个enum
外, 基本上可以把enum
当作一个普通的类来处理, 也就是说可以向enum
中添加方法, 比如返回其自身描述的方法, 还可以添加main方法.
下面是一个演示enum
添加自定义方法和实现接口的示例:
(1) 定义一个对象描述信息的接口:
interface ObjectDescription {
String todo();
}
(2) 创建枚举类:
public enum Signal implements ObjectDescription {
Red("红灯", "敢过去就是6分, 还要罚款哦"),
Yellow("黄灯", "黄灯你也不敢过"),
Green("绿灯", "绿灯也得小心过啊");
private String name;
private String description;
/**
* 构造方法, 对内部变量进行初始化
*/
Signal(String name, String description) {
this.name = name;
this.description = description;
}
private String getName() {
return name;
}
private String getDescription() {
return description;
}
@Override
public String todo() {
return "Signal类用于表示交通信号灯, [" + this + "] 表示 [" + this.getName() + "]";
}
public static void main(String[] args) {
// 调用实现的接口中的方法
for (Signal signal : Signal.values()) {
System.out.println(signal.todo());
}
// 调用自定义的方法
for (Signal signal : Signal.values()) {
System.out.println(signal.getName() + ": " + signal.getDescription());
}
}
}
(3) 运行结果如下:
Signal类用于表示交通信号灯, [Red] 表示 [红灯]
Signal类用于表示交通信号灯, [Yellow] 表示 [黄灯]
Signal类用于表示交通信号灯, [Green] 表示 [绿灯]
红灯: 敢过去就是6分, 还要罚款哦
黄灯: 黄灯你也不敢过
绿灯: 绿灯也得小心过啊
使用注意事项:
a) 如果要自定义方法, 就必须在enum实例序列的最后添加一个分号, 同时Java要求必须先定义enum实例, 否则编译时会报错.
b)
enum
的构造器只能是private
, 它只能在enum
内部被用来创建enum
实例, 一旦enum
定义结束, 编译器就不允许再使用其构造器来创建任何实例了.
3 接口内部创建枚举
无法使用继承限制了枚举的使用, 比如需要用enum
表示食物, 但同时需要区分水果、点心等类型, 这个时候就没不够灵活了.
我们通过在一个接口内部创建实现该接口的枚举, 从而达到对元素进行分类组织的目的:
public interface Food {
/**
* 开胃菜
*/
enum Appetizer implements Food {
SALAD, SOUP, SPRING_ROLLS;
}
/**
* 主菜
*/
enum MainCourse implements Food {
RICE, NOODLES, VINDALOO, BEEF;
}
/**
* 甜品
*/
enum Dessert implements Food {
TIRAMISU, ICE_CREAM, BISCUIT, FRUIT;
}
/**
* 咖啡
*/
enum Coffee implements Food {
BLACK_COFFEE, DECAF_COFFEE, LATTE;
}
}
对enum
而言, 实现接口是使其子类化的唯一方法. 通过上面的形式, 成功地完成了对不同食物的分组, 并且它们都是Food.
4 枚举类中使用枚举
下面是一个枚举的随机选择器, 是一个工具类:
public class Enums {
private static Random rand = new Random(47);
public static <T extends Enum<T>> T random(Class<T> enumClazz) {
return random(enumClazz.getEnumConstants());
}
public static <T> T random(T[] values) {
return values[rand.nextInt(values.length)];
}
}
结合工具类及上面Food接口的内容, 下面通过枚举的枚举实现一个产生随机菜单的例子:
public enum Course {
APPETIZER(Food.Appetizer.class),
MAINCOURSE(Food.MainCourse.class),
DESSERT(Food.Dessert.class),
COFFEE(Food.Coffee.class);
private Food[] values;
Course(Class<? extends Food> kind) {
// 返回枚举中所有的元素, 及所有实例构成的数组, 如果kind不是枚举返回null
values = kind.getEnumConstants();
}
public Food randomSelection() {
return Enums.random(values);
}
public static void main(String[] args) {
// 产生5份随机菜单
for (int i = 0; i < 5; i++) {
for (Course c : Course.values()) {
Food food = c.randomSelection();
System.out.println(food + " ");
}
System.out.println("---------------");
}
}
}
5 扩展: 验证values()不是通过父类继承的
下面的代码用来验证values()
方法是enum
自身的, 而不是继承自父类java.lang.Enum
的:
public enum Signal implements ObjectDescription {
Red("红灯", "敢过去就是6分, 还要罚款哦"),
Yellow("黄灯", "黄灯你也不敢过"),
Green("绿灯", "绿灯也得小心过啊");
private String name;
private String description;
Signal(String name, String description) {
this.name = name;
this.description = description;
}
private String getName() {
return name;
}
private String getDescription() {
return description;
}
@Override
public String todo() {
return "Signal类用于表示交通信号灯, [" + this + "] 表示 [" + this.getName() + "]";
}
public static void main(String[] args) {
Set<Method> methodSet = new HashSet<Method>();
// 获取本类的所有方法
Method[] signalMethods = Signal.class.getMethods();
for (Method m : signalMethods) {
methodSet.add(m);
}
// 获取父类中的方法
Method[] superClassMethods = Signal.class.getSuperclass().getMethods();
// 去除本类中继承的父类方法
for (Method m : superClassMethods) {
methodSet.remove(m);
}
// 遍历输出本类中独有的方法
for(Method m : methodSet) {
System.out.println(m);
}
}
}
结果如下, 其中各个字段的含义依次为访问权限 [是否静态] 返回值类型的全限定名称 方法的全限定名称
:
public static com.healchow.Signal com.healchow.Signal.valueOf(java.lang.String)
public static com.healchow.Signal[] com.healchow.Signal.values()
public static void com.healchow.Signal.main(java.lang.String[])
public java.lang.String com.healchow.Signal.todo()
版权声明
本文版权归原作者所有, 如有侵权, 请联系博主, 定当立即删除.
若要转载, 请在文章页面明显位置标明原始链接, 否则一切责任自负.