【问题标题】:Generic interface for enums in JavaJava中枚举的通用接口
【发布时间】:2017-07-16 14:53:05
【问题描述】:

我在 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 是静态方法,似乎不可能创建返回泛型类型的静态方法。

这个问题有解决办法吗?

【问题讨论】:

  • 似乎不可能创建返回泛型类型的静态方法 - stackoverflow.com/questions/4409100/…
  • 我只是想知道为什么你必须同时使用构造函数和工厂方法?
  • 你为什么要为你的枚举创建一个转换器? Hibernate 应该能够正确处理这种情况!将枚举作为字符串保存到数据库并将字符串从数据库转换为枚举!您只需要使用 @Enumerated 注释。还是不是这样?
  • 顺便说一下,枚举已经可以在它们的字符串表示之间进行转换,而无需存储额外的东西(使用enumValue.name()enumValue.toString() 的枚举到字符串;以及使用YourEnumType.valueOf(string) 或的字符串到枚举) Enum.valueOf(clazz, string))。内置字符串表示是枚举的确切编写方式,在您的情况下是全大写,而您想要的是首字母大写,所以也许您可以进行一些简单的大小写转换。

标签: java generics enums


【解决方案1】:

问题是我不想为每个枚举创建转换器 理想情况下,它应该是通用类/接口,我将使用 这里是多态性。

您别无选择,因为当您注释实体时,您的 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】:

    如果您想为所有 enum 类使用通用转换器,则可以使用反射,只要您遵守命名约定。

    您的约定似乎是您使用toString() 进行enum -> String 转换,并使用static fromString(String) 进行String -> enum 转换。

    一个转换器是这样的:

    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)
    

    【讨论】:

      【解决方案3】:

      您应该能够做到以下几点:

      public class Converter<T extends Enum<T>, U> implements AttributeConverter<T, U> {
      
          public U convertToDatabaseColumn(T value) {
              ...
          }
      
          public T convertToEntityAttribute(U value) {
              ...
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-06-02
        • 1970-01-01
        • 1970-01-01
        • 2014-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多