在 Java 中扩展枚举
本文介绍如何在 Java 中扩展枚举功能。
在 Java 中扩展枚举
我们可以将枚举视为一种编译器魔法,因为在字节码中,枚举表示为具有多个静态成员的类,并且继承自抽象 java.lang.Enum。
这就是枚举不能扩展任何其他类或枚举的原因。 正如我们无法在 Java 中扩展枚举一样,任何类也不可能扩展枚举。 让我们使用以下示例代码来学习,看看会发生什么。
示例代码:
package jiyik;
enum Cars {Audi, BMW, Marcedes}
public class Example extends Cars {
public static void main(String... args) {
}
}
上面的代码有一个名为 Cars 的枚举,Example 类正在尝试扩展它。 查看输出:
/Example.java:5: error: cannot inherit from final Cars
public class Example extends Cars {
^
/Example.java:5: error: enum types are not extensible
public class Example extends Cars {
^
2 errors
正如我们所看到的,类不能扩展枚举。 那么如果无法扩展枚举,我们还能扩展它的功能吗?
功能可以定义为枚举中实现的方法。 例如,上面代码中的枚举 Cars 可以为每个成员声明抽象方法; 参见示例:
enum Cars {
Audi { @Override public void drive() { } },
BMW { @Override public void drive() { } },
Mercedes { @Override public void drive() { } },
;
public abstract void drive();
}
正如我们所看到的,每个成员都可以重写drive()方法。 但不幸的是,并不总是可以在枚举中创建方法,因为:
- 如果枚举属于第三方库或其他团队,则它将不允许实现抽象方法。
- 如果它属于不具有drive()方法所需的依赖项的模块。
- 如果枚举被其他函数和数据重载,它将变得不可读。
提供了一些解决方案可以解决这些问题并扩展 Java 中枚举的功能。
解决方案1:Java中的镜像枚举
顾名思义,我们需要使用相同的数据创建另一个枚举。 这个新的枚举也将实现drive()方法,所以我们现在有两个枚举:
枚举 Cars 的示例代码:
enum Cars {
Audi { @Override public void drive() { } },
BMW { @Override public void drive() { } },
Mercedes { @Override public void drive() { } },
;
public abstract void drive();
}
枚举 DriveCars 的示例代码:
enum DriveCars {
Audi { @Override public void drive() { } },
BMW { @Override public void drive() { } },
Mercedes { @Override public void drive() { } },
;
public abstract void drive();
}
第二个枚举是第一个枚举的镜像。 现在我们可以通过扩展功能来使用这两个枚举; 我们需要使用内置枚举方法 name()
和 valueof()
。
请参阅以下示例了解如何使用它:
Cars cars = ...
DriveCars.valueOf(cars.name()).drive();
上面的代码显示了如何在枚举 DriveCars 中使用枚举 Cars 功能。 这意味着功能得到了扩展。
上面代码中的 name()
方法是final的,不能被重写,而valueOf方法将由编译器生成。 这两种方法非常适合,在扩展操作中不会出现功能错误。
上面的代码有一个问题,如果枚举Cars改变了,枚举DriveCars将不知道任何想法,并且会导致trick的名称和值失败。 为了解决这个问题,我们必须让DriveCars知道它的父镜像是Cars。
为此,我们可以使用静态初始化程序来比较 DriveCars 和 Cars,如果两个枚举不匹配,则会抛出异常。 这是 enumus 库中的一个示例:
enum DriveCars {
....
static {
Mirror.of(Cars.class);
}
}
该实用程序类将检查两个枚举是否匹配。 此方法将验证 name() 和 valueOf() 技巧。
解决方案 2:Java 中的映射枚举
如果您不想创建另一个仅包含一种方法的枚举。 在这种情况下,我们可以使用接口来代替枚举; 请参见下面的示例:
public interface Driver {
void drive();
}
现在要使用此接口 Drive 和枚举 Cars,我们可以在它们之间创建映射。 这是地图的示例:
Map<Cars, Driver> drivers = new EnumMap<>(Cars.class) {{
put(Audi, new Driver() { @Override public void driver(){}})
put(BMW, new Driver() { @Override public void driver(){}})
put(Mercedes, new Driver() { @Override public void driver(){}})
}}
现在要使用它们,请使用这段简单的代码:
drivers.get(Cars).drive();
上面代码中使用的 EnumMap 将保证每个枚举成员仅出现一次,但它不保证每个成员都有一个条目。
我们可以检查映射的大小是否等于枚举成员的数量:
drivers.size() == Cars.values().length
enumus 库还为这种情况提供了一个实用程序:如果地图不适合汽车,它将抛出 IllegalStateException。 这是实用程序:
EnumMapValidator.validateValues(Cars.class, map, "Cars map");
上述两种方法都展示了如何通过扩展枚举的功能来使枚举变得强大。 虽然不可能直接扩展枚举,但我们可以使用这些技巧来扩展它们的功能。
相关文章
如何在 Java 中延迟几秒钟的时间
发布时间:2023/12/17 浏览次数:217 分类:Java
-
本篇文章主要介绍如何在 Java 中制造程序延迟。本教程介绍了如何在 Java 中制造程序延时,并列举了一些示例代码来了解它。
如何在 Java 中把 Hashmap 转换为 JSON 对象
发布时间:2023/12/17 浏览次数:187 分类:Java
-
它描述了允许我们将哈希图转换为简单的 JSON 对象的方法。本文介绍了在 Java 中把 Hashmap 转换为 JSON 对象的方法。我们将看到关于创建一个 hashmap,然后将其转换为 JSON 对象的详细例子。
如何在 Java 中按值排序 Map
发布时间:2023/12/17 浏览次数:171 分类:Java
-
本文介绍了如何在 Java 中按值对 Map 进行排序。本教程介绍了如何在 Java 中按值对 Map
进行排序,并列出了一些示例代码来理解它。
如何在 Java 中打印 HashMap
发布时间:2023/12/17 浏览次数:192 分类:Java
-
本帖介绍了如何在 Java 中打印 HashMap。本教程介绍了如何在 Java 中打印 HashMap 元素,还列举了一些示例代码来理解这个主题。
在 Java 中更新 Hashmap 的值
发布时间:2023/12/17 浏览次数:146 分类:Java
-
本文介绍了如何在 Java 中更新 HashMap 中的一个值。本文介绍了如何在 Java 中使用 HashMap 类中包含的两个方法-put() 和 replace() 更新 HashMap 中的值。
Java 中的 hashmap 和 map 之间的区别
发布时间:2023/12/17 浏览次数:79 分类:Java
-
本文介绍了 Java 中的 hashmap 和 map 接口之间的区别。本教程介绍了 Java 中 Map 和 HashMap 之间的主要区别。在 Java 中,Map 是用于以键值对存储数据的接口,
在 Java 中获取用户主目录
发布时间:2023/12/17 浏览次数:218 分类:Java
-
这篇文章向你展示了如何在 Java 中获取用户主目录。本教程介绍了如何在 Java 中获取用户主目录,并列出了一些示例代码以指导你完成该主题。
Java 中 size 和 length 的区别
发布时间:2023/12/17 浏览次数:179 分类:Java
-
这篇文章教你如何知道 Java 中大小和长度之间的区别。本教程介绍了 Java 中大小和长度之间的区别。我们还列出了一些示例代码以帮助你理解该主题。
Java 中的互斥锁
发布时间:2023/12/17 浏览次数:111 分类:Java
-
了解有关 Java 中互斥锁的一切,在计算机科学领域,互斥或互斥被称为并发控制的属性。每台计算机都使用称为线程的最小程序指令序列。有一次,计算机在一个线程上工作。为了更好地理解,