是否可以在Java 8中扩展枚举?

只是玩,并想出一个甜蜜的方式来添加Java Enum toString()方法中的 enumfunction。

一些进一步的修补让我几乎也添加了一个整洁(即没有抛出exception)反向查找,但有一个问题。 据报道:

 error: valueOf(String) in X cannot implement valueOf(String) in HasValue public enum X implements PoliteEnum, ReverseLookup { overriding method is static 

有办法吗?

这里的目的是静默添加(通过接口实现,使用default方法,如我在链接的答案中添加了politeName )一个lookup方法,该方法执行valueOf函数而不抛出exception。 可能吗? 显然现在可以扩展enum – 直到现在我的Java主要问题之一。

这是我失败的尝试:

 public interface HasName { public String name(); } public interface PoliteEnum extends HasName { default String politeName() { return name().replace("_", " "); } } public interface Lookup

{ public Q lookup(P p); } public interface HasValue { HasValue valueOf(String name); } public interface ReverseLookup extends HasValue, Lookup { @Override default HasValue lookup(String from) { try { return valueOf(from); } catch (IllegalArgumentException e) { return null; } } } public enum X implements PoliteEnum/* NOT ALLOWED :( , ReverseLookup*/ { A_For_Ism, B_For_Mutton, C_Forth_Highlanders; } public void test() { // Test the politeName for (X x : X.values()) { System.out.println(x.politeName()); } // ToDo: Test lookup }

你的设计太复杂了。 如果您愿意接受只能在实例上调用default方法,那么整个代码可能如下所示:

 interface ReverseLookupSupport> { Class getDeclaringClass(); default E lookup(String name) { try { return Enum.valueOf(getDeclaringClass(), name); } catch(IllegalArgumentException ex) { return null; } } } enum Test implements ReverseLookupSupport { FOO, BAR } 

你可以测试它:

 Test foo=Test.FOO; Test bar=foo.lookup("BAR"), baz=foo.lookup("BAZ"); System.out.println(bar+" "+baz); 

非投掷/捕捉替代方案是:

 interface ReverseLookupSupport> { Class getDeclaringClass(); default Optional lookup(String name) { return Stream.of(getDeclaringClass().getEnumConstants()) .filter(e->e.name().equals(name)).findFirst(); } 

使用像:

 Test foo=Test.FOO; Test bar=foo.lookup("BAR").orElse(null), baz=foo.lookup("BAZ").orElse(null); System.out.println(bar+" "+baz); 

这里基本上有两点。 具体而言,它不编译的原因是8.4.8.1 :

如果实例方法覆盖静态方法,则为编译时错误。

换句话说,由于名称冲突,枚举无法实现HasValue。

然后我们遇到的更普遍的问题是静态方法不能被“覆盖”。 由于valueOf是编译器在Enum派生类本身上插入的静态方法,因此无法更改它。 我们也不能使用接口来解决它,因为它们没有静态方法。

在这个特定的情况下,它是一个组合可以使这种东西减少重复的地方,例如:

 public class ValueOfHelper> { private final Map map = new HashMap(); public ValueOfHelper(Class cls) { for(E e : EnumSet.allOf(cls)) map.put(e.name(), e); } public E valueOfOrNull(String name) { return map.get(name); } } public enum Composed { A, B, C; private static final ValueOfHelper HELPER = ( new ValueOfHelper(Composed.class) ); public static Composed valueOfOrNull(String name) { return HELPER.valueOfOrNull(name); } } 

(另外,无论如何,我建议过去捕捉exception。)

我意识到“你不能做到”并不是一个理想的答案,但由于静态方面的原因,我没有看到解决方法。

情况与您在界面中无法创建默认的toString()相同。 枚举已包含静态valueOf(String)方法的签名,因此您无法覆盖它。

枚举是编译时常量,因此它确实有可能在某一天可扩展。

如果你想获得常量名称,你可以使用:

 public static > Optional valueFor(Class type, String name) { return Arrays.stream(type.getEnumConstants()).filter( x -> x.name().equals(name)).findFirst(); } 

我想我有一个答案 – 它是hacky并且使用reflection但似乎适合简短 – 即在枚举中没有方法的反向查找并且没有抛出exception。

 public interface HasName { public String name(); } public interface PoliteEnum extends HasName { default String politeName() { return name().replace("_", " "); } } public interface Lookup { public Q lookup(P p); } public interface ReverseLookup> extends Lookup { @Override default T lookup(String s) { return (T) useMap(this, s); } } // Probably do somethiong better than this in the final version. static final Map theMap = new HashMap<>(); static Enum useMap(Object o, String s) { if (theMap.isEmpty()) { try { // Yukk!! Enum it = (Enum)o; Class c = it.getDeclaringClass(); // Reflect to call the static method. Method method = c.getMethod("values"); // Yukk!! Enum[] enums = (Enum[])method.invoke(null); // Walk the enums. for ( Enum e : enums) { theMap.put(e.name(), e); } } catch (Exception ex) { // Ewwww } } return theMap.get(s); } public enum X implements PoliteEnum, ReverseLookup { A_For_Ism, B_For_Mutton, C_Forth_Highlanders; } public void test() { for (X x : X.values()) { System.out.println(x.politeName()); } for (X x : X.values()) { System.out.println(x.lookup(x.name())); } } 

版画

 A For Ism B For Mutton C Forth Highlanders A_For_Ism B_For_Mutton C_Forth_Highlanders 

添加

受到@Holger的启发 – 这就是我认为最喜欢的东西:

 public interface ReverseLookup> extends Lookup { // Map of all classes that have lookups. Map> lookups = new ConcurrentHashMap<>(); // What I need from the Enum. Class getDeclaringClass(); @Override default E lookup(String name) throws InterruptedException, ExecutionException { // What class. Class c = getDeclaringClass(); // Get the map. final Map lookup = lookups.computeIfAbsent(c, k -> Stream.of(c.getEnumConstants()) // Roll each enum into the lookup. .collect(Collectors.toMap(Enum::name, Function.identity()))); // Look it up. return c.cast(lookup.get(name)); } } // Use the above interfaces to add to the enum. public enum X implements PoliteName, ReverseLookup { A_For_Ism, B_For_Mutton, C_Forth_Highlanders; }