是否可以在Java 8中扩展枚举?
只是玩,并想出一个甜蜜的方式来添加Java Enum toString()方法中的 enum
function。
一些进一步的修补让我几乎也添加了一个整洁(即没有抛出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; }