基于值返回枚举的Java泛型

基于值返回枚举的Java泛型,java,generics,enums,java-8,Java,Generics,Enums,Java 8,我有许多枚举在它们之间共享该方法。如果可能的话,我想把它移到一个接口上,这样我就不必复制它,代码看起来会更干净。但是经过很多努力,我仍然无法将方法移动到接口 public enum TypeA { ValueAA ("Value AA"), ValueAB ("Value AB"); private final String type; TypeA (final String type) { this.type = type; }

我有许多枚举在它们之间共享该方法。如果可能的话,我想把它移到一个接口上,这样我就不必复制它,代码看起来会更干净。但是经过很多努力,我仍然无法将方法移动到接口

public enum TypeA {
    ValueAA ("Value AA"),
    ValueAB ("Value AB");

    private final String type;

    TypeA (final String type) {
        this.type = type;
    }

    @JsonValue
    public String getType() {
        return this.type;
    }

    @JsonCreator
    public static TypeA fromValue(final String value) {
        for (TypeA t : TypeA.values()) {
            if (t.getType().equalsIgnoreCase(value)) {
                return t;
            }
        }

        StringBuilder allTypes = new StringBuilder();
        boolean bFirstTime = true;
        for (TypeA val : TypeA.values()) {
            allTypes.append(bFirstTime ? "" : ", ").append(val);
            bFirstTime = false;
        }

        throw new IllegalArgumentException(value + " is an invalid value. Supported values are " + allTypes);
    }
}

public enum TypeB {
    ValueBA ("Value BA"),
    ValueBB ("Value BB");

    private final String type;

    TypeB (final String type) {
        this.type = type;
    }

    @JsonValue
    public String getType() {
        return this.type;
    }

    @JsonCreator
    public static TypeB fromValue(final String value) {
        for (TypeB t : TypeB.values()) {
            if (t.getType().equalsIgnoreCase(value)) {
                return t;
            }
        }

        StringBuilder allTypes = new StringBuilder();
        boolean bFirstTime = true;
        for (TypeB val : TypeB.values()) {
            allTypes.append(bFirstTime ? "" : ", ").append(val);
            bFirstTime = false;
        }

        throw new IllegalArgumentException(value + " is an invalid value. Supported values are " + allTypes);
    }
}

使用泛型和Java 8是否可以将
getType
fromValue
方法移动到一个接口,以便我可以在所有枚举中共享?还要注意Jackson注释
JsonValue
&
JsonCreator

您可以将
从value
实现移动到
接口
,但是,我想,您必须在具体类型中保留存根以支持JSON工厂注释:

interface TypeX {
    String getType();
    static <T extends Enum<T>&TypeX> T fromValue(String value, Class<T> type) {
        EnumSet<T> all=EnumSet.allOf(type);
        for (T t: all) {
            if (t.getType().equalsIgnoreCase(value)) {
                return t;
            }
        }
        throw new IllegalArgumentException(all.stream().map(t -> t.getType())
            .collect(Collectors.joining(", ",
                value+" is an invalid value. Supported values are ", "")));
    }
}

public enum TypeA implements TypeX {
    ValueAA ("Value AA"),
    ValueAB ("Value AB");

    private final String type;

    TypeA (final String type) {
        this.type = type;
    }

    @JsonValue
    public String getType() {
        return this.type;
    }

    @JsonCreator
    public static TypeA fromValue(final String value) {
        return TypeX.fromValue(value, TypeA.class);
    }
}

enum TypeB implements TypeX {
    ValueBA ("Value BA"),
    ValueBB ("Value BB");

    private final String type;

    TypeB (final String type) {
        this.type = type;
    }

    @JsonValue
    public String getType() {
        return this.type;
    }

    @JsonCreator
    public static TypeB fromValue(final String value) {
        return TypeX.fromValue(value, TypeB.class);
    }
}

但这有几个缺点,例如,基于反射的访问在编译时未被检查,并且在运行时可能存在性能缺点。我不知道当出现在从
接口继承的
默认方法中时,
@JsonValue
是否会以预期的方式得到尊重。枚举类型不能是您自己类的子类型。感谢您的简单解释,您可以创建一个辅助类来保存状态并在其他类之间共享实例。然而,似乎枚举不是您想要的。您可以滚动自己的类型安全枚举并为它们提供公共代码,或者更好地创建一个状态维护类,该类保存所需的状态并同时保存枚举值。目前还不清楚什么对您最有利,因为您的示例并没有提示您为什么需要枚举,更不用说两种共享状态的枚举了。从表面上看,你可能不会,应该重新考虑你的模型。@Sotirios Delimanolis:这种状态是不变的,接口支持常量。所以在这种特殊情况下,这是可能的。但是,我会按照惯例将
getType
方法保留在
enum
类型中。为什么要使用
TypeA.class
而不是
values()
?@Tom Hawtin:
TypeA.class
是一条廉价的字节码指令,
values()
是一个方法调用,而被调用的方法创建并填充一个新数组。因此,让调用方成为一个廉价的操作,并让
fromValue
封装它的工作方式,即
EnumSet应该是很自然的。allOf(…)
不创建数组
fromValue
甚至可以实现从
String
value到
enum
常量的缓存映射,而不需要在调用方进行任何更改。传入一个数组会适得其反,我认为一个方法调用的假定成本与您为避免它所做的所有工作相比是无关紧要的。还有密码…@Tom Hawtin:我没有做任何“避免它”的事。如果要使用
values()
而不是
EnumSet.allOf
,则只需将
EnumSet
替换为
T[]
,而无需更改任何程序逻辑。仍然没有理由让每个调用方都执行该操作,而不是让单个方法处理它。
interface TypeX {
    @Retention(RetentionPolicy.RUNTIME) @interface Type { String value(); }

    @JsonValue default String getType() {
        for(Field f: getDeclaringClass().getDeclaredFields()) try {
            if(f.isEnumConstant() && f.get(null)==this) {
                return f.getAnnotation(Type.class).value();
            }
        } catch(IllegalAccessException ex) {
            throw new AssertionError(ex);
        }
        throw new IllegalStateException();
    }

    Class<? extends TypeX> getDeclaringClass();

    static <T extends Enum<T>&TypeX> T fromValue(String value, Class<T> type) {
        EnumSet<T> all=EnumSet.allOf(type);
        for (T t: all) {
            if (t.getType().equalsIgnoreCase(value)) {
                return t;
            }
        }
        throw new IllegalArgumentException(all.stream().map(t -> t.getType())
            .collect(Collectors.joining(", ",
                value+" is an invalid value. Supported values are ", "")));
    }
}

public enum TypeA implements TypeX {
    @Type("Value AA") ValueAA,
    @Type("Value AB") ValueAB;

    @JsonCreator
    public static TypeA fromValue(final String value) {
        return TypeX.fromValue(value, TypeA.class);
    }
}

enum TypeB implements TypeX {
    @Type("Value BA") ValueBA,
    @Type("Value BB") ValueBB;

    @JsonCreator
    public static TypeB fromValue(final String value) {
        return TypeX.fromValue(value, TypeB.class);
    }
}