如何在不覆盖方法的情况下在枚举中实现接口(带参数)

How to implement Interface in Enums (with parameters) without overriding methods

我有如下 Enumutils:

public interface EnumUtil {
    String getValue();

    static <T extends Enum<T> & EnumUtil> T fromValue(String enumValue, Class<T> type) {
        EnumSet<T> all=EnumSet.allOf(type);
        for (final T t: all) {
            System.out.println("Value: " + t.getValue());
            System.out.println("Name: " + t.name());

            T val = T.valueOf(t.getDeclaringClass(), t.name()).;
            System.out.println("ValValVal: " + val);

            if (t.getValue().equalsIgnoreCase(enumValue)) {
                return t;
            }
        }
        return null;
    }
}

然后我创建了一个枚举,如下所示:

@Getter 
@AllArgsConstructor
public enum SupportedOptions implements EnumUtil {
    PART("PART"),
    MSRV("MSRV");

    private final String value; //If we add this line then we need not to override getValue()

    public static SupportedOptions fromValue(final String text) {
        return EnumUtil.fromValue(text, SupportedOptions.class);
    }
}

这工作正常,没有任何编译问题。 (getValue 也returns 枚举参数在 EnumUtils 接口的 fromvalue 中成功用于此枚举)

但在下面的场景中会导致编译时异常(需要实现抽象方法)

@Getter 
@AllArgsConstructor
public enum RejectedResponseCode implements EnumUtil {
    UNPR("300", "849", "700", "701", "702", "703", "705", "730","704"),
    IMSG("302", "105", "113", "114"),
    PARS("107", "100", "102", "103", "115","720"),
    SECU("302", "668", "669", "670", "671");

    //approach 1
    private final String value; //Here it want some value as i am using constructor like below

    RejectedResponseCode(final String... codes) {
        this.codes = Arrays.asList(codes);
        //this.value = getValue(); <--- Is this approach fine. (For appraoch 1)
    }

    private final List<String> codes;

    public static RejectedResponseCode getValueOfData(final String value) {
        final Optional<RejectedResponseCode> result =  Arrays.stream(values()).filter(rejectedResponseCode -> rejectedResponseCode.codes.contains(value)).findFirst();
        if (result.isPresent()) {
            return result.get();
        }
        return null;
    }

    public static RejectedResponseCode fromValue(final String text) {
        return EnumUtil.fromValue(text, RejectedResponseCode.class);
    }

    //Approach 2 
    //@Override
    //public String getValue(){
    //  return null; //Is this approach correct?
    //}
}

如何忽略不覆盖getValue()? 或者如何以 returns 参数的方式覆盖 getValue() 或任何其他方式在 EnumUtil 中管理它?

按照 Joachim Sauer 在评论中的指示:

EnumUtil.fromValue() 简单地假设每个枚举值始终只有一个标识字符串。对于您的第二个样本,情况并非如此。所以要么不要使用 EnumUtil.fromValue() 要么扩展它来支持多个值(可能通过有第二个接口可以 return 数组或潜在标识符的集合)–

因此,EnumUtils 从枚举中删除并仅在枚举内部创建方法。

您需要搜索支持多个搜索键的每个枚举值,因此实现 getValue 是没有用的。解决这个问题的方法是反转操作,使枚举提供一组要使用的搜索条件,并在 EnumUtil 上实现一个简单的实用方法:

public class EnumUtil {
    @SafeVarargs
    public static <T,V> T match(T[] values, V fieldValue, BiPredicate<T,V> ... checks) {
        for (var pred : checks)
            for (T item : values)
                if(pred.test(item, fieldValue))
                    return item;

        return null; // OR throw new IllegalArgumentException("Not found: "+fieldValue);
    }
}

然后您的枚举 类 可以设置任意数量的搜索参数。 SupportedOptions 仅匹配名称:

enum SupportedOptions {
    PART,
    MSRV;
    public static SupportedOptions fromValue(final String text) {
        return EnumUtil.match(values(), text.toUpperCase(), (e, s) -> e.name().equals(s));
    }
}

RejectedResponseCode 在 name()codes.indexOf 上匹配:

enum RejectedResponseCode {
    UNPR("300", "849", "700", "701", "702", "703", "705", "730","704"),
    IMSG("302", "105", "113", "114"),
    PARS("107", "100", "102", "103", "115","720"),
    SECU("302", "668", "669", "670", "671");

    RejectedResponseCode(final String... codes) {
        this.codes = Arrays.asList(codes);
    }
    private final List<String> codes;

    public static RejectedResponseCode fromValue(final String text) {
        return EnumUtil.match(values(), text.toUpperCase(), (e, s) -> e.name().equals(s), (e, s) -> e.codes.indexOf(s) >= 0);
    }
}

所以,任何东西都可以通过名称定位:

public static void main(String[] args)
{
    for (String s : new String[] { "PART", "msrv", "other"})
        System.out.println("SupportedOptions.fromValue("+s+") => "+ SupportedOptions.fromValue(s));

    for (String s : new String[] { "UNPR", "imsg", "102", "671", "999999"})
        System.out.println("RejectedResponseCode.fromValue("+s+") => "+ RejectedResponseCode.fromValue(s));
}

SupportedOptions.fromValue(PART) => PART
SupportedOptions.fromValue(msrv) => MSRV
SupportedOptions.fromValue(other) => null
RejectedResponseCode.fromValue(UNPR) => UNPR
RejectedResponseCode.fromValue(imsg) => IMSG
RejectedResponseCode.fromValue(102) => PARS
RejectedResponseCode.fromValue(671) => SECU
RejectedResponseCode.fromValue(999999) => null