Java中用于枚举的通用接口

5

我有一个基于Hibernate / Spring的Web应用程序,其中有几个枚举类型,我想在应用程序中使用它们。

public enum MerchantStatus {
    NEW("New"),
    ...

    private final String status;

    MerchantStatus(String status) {
        this.status = status;
    }

    public static MerchantStatus fromString(String status) {..}    

    public String toString() {..}
}

并且

public enum EmployerType {
    COOL("Cool"),
    ...

    private final String type;

    EmployerType (String type) {
        this.type = type;
    }

    public static EmployerType fromString(String type) {..}    

    public String toString() {..}
}

我希望创建一个转换器,将我的枚举对象转换为字符串并反之亦然。大致是这样的:
public class MerchantStatusConverter implements AttributeConverter<MerchantStatus, String> {
    public String convertToDatabaseColumn(MerchantStatus value) {..}

    public MerchantStatus convertToEntityAttribute(String value) {..}
}

问题在于我不想为每个枚举类型创建转换器,理想情况下应该是一个通用的类/接口,并且我将在这里使用多态。问题是fromString是静态方法,似乎不可能创建返回通用类型的静态方法。
有没有解决这个问题的办法?

1
似乎无法创建返回通用类型的静态方法。 - https://dev59.com/yG855IYBdhLWcg3wVikt - BackSlash
我只是想知道为什么你必须同时使用构造函数和工厂方法? - phatnhse
你为什么要创建一个枚举转换器呢?Hibernate应该能够正确处理这种情况!将枚举保存为字符串到数据库中,并将字符串从数据库转换为枚举!你只需要使用@Enumerated注解即可。或者不是这种情况吗? - rafaelim
顺便说一下,枚举类型已经可以在不需要存储额外信息的情况下转换为其字符串表示形式(使用 enumValue.name()enumValue.toString() 将枚举转换为字符串;使用 YourEnumType.valueOf(string)Enum.valueOf(clazz, string) 将字符串转换为枚举)。内置的字符串表示形式就是枚举的确切写法,而在您的情况下是全大写,而您想要的是首字母大写,因此您可以进行一些简单的大小写转换。 - newacct
3个回答

5
问题是我不想为每个枚举创建一个转换器,理想情况下应该是通用的类/接口,并且我将在这里使用多态。
由于在注释实体时无法对AttributeConverter实现进行参数化,因此您只能使用AttributeConverter类来指定它。
@Enumerated(EnumType.STRING)
@Convert(converter = MerchantStatusConverter.class)
private MerchantStatus merchantStatus;

但是您可以定义一个抽象类来定义逻辑,并将其子类化在每个枚举类中。
为了实现它,您应该在每个枚举类前面引入一个接口,声明一个fromString()和一个toString()方法。

接口:

public interface MyEnum<T extends MyEnum<T>>{

     T fromString(String type);
     String toString(T enumValue);
}

实现该接口的枚举类型:
public enum MerchantStatus implements MyEnum<MerchantStatus> {

    NEW("New"), ...


    @Override
    public MerchantStatus fromString(String type) {
     ...
    }

    @Override
    public String toString(MerchantStatus enumValue) {
     ...
    }
}

抽象的 AttributeConverter 类:
public abstract class AbstractAttributeConverter<E extends MyEnum<E>> implements AttributeConverter<E, String> {

    protected MyEnum<E> myEnum;

    @Override
    public String convertToDatabaseColumn(E attribute) {
        return myEnum.toString(attribute);
    }

    @Override
    public E convertToEntityAttribute(String dbData) {
        return myEnum.fromString(dbData);
    }
}

还有一个具体的AttributeConverter类需要声明一个公共构造函数,将受保护的myEnum字段分配给枚举值(无论哪个):

public class MerchantStatusAttributeConverter extends AbstractAttributeConverter<MerchantStatus> {
   public MerchantStatusAttributeConverter(){
      myEnum = MerchantStatus.NEW; 
   }
}

2
如果您想为所有的枚举类使用通用转换器,可以使用反射,只要遵守命名约定即可。
您的约定似乎是将 toString() 用于枚举 -> String 转换,并使用 static fromString(String) 进行 String -> 枚举 转换。
这样的转换器应该像这样:
public class EnumConverter<T extends Enum<T>> implements AttributeConverter<T, String> {
    private final Method fromStringMethod;

    public EnumConverter(Class<T> enumClass) {
        try {
            this.fromStringMethod = enumClass.getDeclaredMethod("fromString", String.class);
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
        if (! Modifier.isStatic(this.fromStringMethod.getModifiers()))
            throw new NoSuchMethodError("fromString(String) is not static");
        if (this.fromStringMethod.getReturnType() != enumClass)
            throw new NoSuchMethodError("fromString(String) does not return " + enumClass.getName());
    }

    public String convertToDatabaseColumn(T value) {
        return value.toString();
    }

    @SuppressWarnings("unchecked")
    public T convertToEntityAttribute(String value) {
        try {
            return (T) this.fromStringMethod.invoke(null, value);
        } catch (IllegalAccessException e) {
            throw new IllegalAccessError(e.getMessage());
        } catch (InvocationTargetException e) {
            throw new RuntimeException("Error calling fromString(String): " + e, e);
        }
    }
}

您需要构建一个类,可以通过为其命名来完成,例如:
new EnumConverter<>(MerchantStatus.class)

new EnumConverter<>(EmployerType.class)

1
你应该能够做到以下事情:
public class Converter<T extends Enum<T>, U> implements AttributeConverter<T, U> {

    public U convertToDatabaseColumn(T value) {
        ...
    }

    public T convertToEntityAttribute(U value) {
        ...
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接