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

时间:2014-02-28 00:22:19

标签: java enums extend java-8

只是玩游戏,然后想出一个甜蜜的方式,在Java Enum toString() method this {{}}} enum中为error: valueOf(String) in X cannot implement valueOf(String) in HasValue public enum X implements PoliteEnum, ReverseLookup { overriding method is static 添加功能。

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

default

有办法吗?

这里的目的是默默地添加(通过界面实现,使用politeName方法,例如我在链接的答案中添加了lookupvalueOf方法执行enum函数没有抛出异常。可能吗?现在显然可以扩展public interface HasName { public String name(); } public interface PoliteEnum extends HasName { default String politeName() { return name().replace("_", " "); } } public interface Lookup<P, Q> { public Q lookup(P p); } public interface HasValue { HasValue valueOf(String name); } public interface ReverseLookup extends HasValue, Lookup<String, HasValue> { @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 } - 我迄今为止的一个主要Java问题。

这是我失败的尝试:

{{1}}

4 个答案:

答案 0 :(得分:15)

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

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

您可以使用以下方法进行测试:

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

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

interface ReverseLookupSupport<E extends Enum<E>> {
    Class<E> getDeclaringClass();
    default Optional<E> 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);

答案 1 :(得分:0)

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

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

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

public static <E extends Enum<E>> Optional<E> valueFor(Class<E> type, String name) {

       return Arrays.stream(type.getEnumConstants()).filter( x ->  x.name().equals(name)).findFirst();

    }

答案 2 :(得分:0)

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

  

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

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

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

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

public class ValueOfHelper<E extends Enum<E>> {
    private final Map<String, E> map = new HashMap<String, E>();

    public ValueOfHelper(Class<E> 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<Composed> HELPER = (
        new ValueOfHelper<Composed>(Composed.class)
    );

    public static Composed valueOfOrNull(String name) {
        return HELPER.valueOfOrNull(name);
    }
}

(另外,我建议过去捕捉异常。)

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

答案 3 :(得分:0)

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

public interface HasName {

    public String name();
}

public interface PoliteEnum extends HasName {

    default String politeName() {
        return name().replace("_", " ");
    }
}

public interface Lookup<P, Q> {

    public Q lookup(P p);
}

public interface ReverseLookup<T extends Enum<T>> extends Lookup<String, T> {

    @Override
    default T lookup(String s) {
        return (T) useMap(this, s);
    }

}

// Probably do somethiong better than this in the final version.
static final Map<String, Enum> 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<X> {

    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<E extends Enum<E>> extends Lookup<String, E> {

  // Map of all classes that have lookups.
  Map<Class, Map<String, Enum>> lookups = new ConcurrentHashMap<>();

  // What I need from the Enum.
  Class<E> getDeclaringClass();

  @Override
  default E lookup(String name) throws InterruptedException, ExecutionException {
    // What class.
    Class<E> c = getDeclaringClass();
    // Get the map.
    final Map<String, Enum> 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<X> {

    A_For_Ism,
    B_For_Mutton,
    C_Forth_Highlanders;
}