Java泛型中的枚举类型

8
我想创建一个基于枚举的通用IBatis映射器。我使用以下代码来实现。这段代码存在编译时错误,我不知道如何修复。也许我的解决方案是错误的(请记住使用IBatis),在这种情况下,请建议更好的解决方案。
非常感谢您的帮助。
我想要实现的目标是定义后续的映射器:
public class XEnumTypeHandler extends CommonEnumTypeHandler<X> {
}

当前代码:

public class CommonEnumTypeHandler<T extends Enum> implements TypeHandlerCallback {

 public void setParameter(ParameterSetter ps, Object o) throws SQLException {
  if (o.getClass().isAssignableFrom(**T**)) { 
   ps.setString(((**T**) o).value().toUpperCase());
  } else
   throw new SQLException("Excpected ParameterType object than: " + o);
 }

 public Object getResult(ResultGetter rs) throws SQLException {
  Object o = valueOf(rs.getString());
  if (o == null)
   throw new SQLException("Unknown parameter type: " + rs.getString());
  return o;
 }

 public Object valueOf(String s) {
  for (T pt : T.**values()**) {
   if (pt.**value()**.equalsIgnoreCase(s))
    return pt;
  }
  return null;
 }
}

我已经在上面的代码中添加了错误标记,错误消息如下:

  • T无法解析
  • 类型T未定义value()方法
  • 类型T未定义values()方法
  • 类型T未定义values()方法

我用以下代码解决了这个问题:

public class CommonEnumTypeHandler<T extends Enum> implements TypeHandlerCallback {

    Class<T> clazz;

    public CommonEnumTypeHandler(Class<T> clazz) {
        this.clazz = clazz;
    }

    public void setParameter(ParameterSetter ps, Object o) throws SQLException {
        if (o.getClass().isAssignableFrom(clazz)) {
            ps.setString(((T) o).name().toUpperCase());
        } else
            throw new SQLException("Excpected " + clazz + " object than: " + o);
    }

    public Object getResult(ResultGetter rs) throws SQLException {
        Object o = valueOf(rs.getString());
        if (o == null)
            throw new SQLException("Unknown parameter type: " + rs.getString());
        return o;
    }

    public Object valueOf(String s) {
        return Enum.valueOf(clazz, s);
    }
}

继承这个类我做如下操作:
public class SalesChannelTypeHandler extends CommonEnumTypeHandler<SalesChannel> {

    public SalesChannelTypeHandler() {
        super(SalesChannel.class);
    }

    public SalesChannelTypeHandler(Class<SalesChannel> clazz) {
        super(clazz);
    }

}

1
请添加编译器错误和一些关于您实际想要实现的描述。 - Dónal
2个回答

8

我不确定你在做什么(一个概述性的描述会很好),但是:

  • 你不能使用 isAssignableFrom(T)(你需要一个 Class 对象),也不能使用 instanceof T(泛型是非具体化的)。你可能想要传递 Class<T> 类型标记。
  • 你是否看过 EnumMap

另请参阅


目前还不清楚需要什么,但可能是类似于这样的东西:

enum Color { BLACK, WHITE; }

public static void main(String[] args) {
    Color c = Enum.valueOf(Color.class, "black".toUpperCase());
    System.out.println(c); // prints "BLACK"
}

所以我们使用Enum.valueOf,它需要一个类型令牌Class<T extends Enum<T>>,并要求它返回给定名称的enum常量。valueOf不区分大小写,但按照约定,所有常量应为大写,因此我们只需取查询字符串并将其转换为.toUpperCase()

@Marcin: 再次提醒,你不能使用T.values(),但是如果你传递Class<T>的类型标记,则可以。我会给出一个示例,但我仍然不确定你在做什么。 - polygenelubricants
只是试图编写一个通用的“从字符串转换为指定类型的枚举”的转换器。传入一个字符串,例如“test”,就可以获取TestEnum.test枚举。 - Marcin Cylke
@Marcin:我理解你想要实现的功能,但是面对现实吧:Java中的泛型不是具体化的!你需要将它作为运行时方法参数传递,而不是编译时泛型类型。也许构造函数会有用? - BalusC
@Marcin:所以你想要一个不区分大小写的Enum.valueOf吗?http://java.sun.com/javase/6/docs/api/java/lang/Enum.html#valueOf%28java.lang.Class,%20java.lang.String%29 - polygenelubricants
@BalusC 好的,现在我明白了,但我不确定您所说的传递 Class<T> 标记是什么意思。您能给我展示一些使用它的例子吗? - Marcin Cylke
@Marcin:本质上,您需要传递“Class”对象的实例。您可以使用.class字面量、Class.forNamesomething.getClass()等方法来获取它们。这些对象存在于运行时,因此它们可以用于支持泛型类型系统(这些类型是非具体化的)。 - polygenelubricants

4

正如Polygenelubricants所指出的那样,您需要传递具体的运行时类型,例如Class<?>,而不是像泛型参数这样的句法编译时类型。以下是重写后您可以使用它的方式:

public abstract class CommonEnumTypeHandler<E extends Enum<E>> implements TypeHandlerCallback {
    private Class<E> enumClass;

    public CommonEnumTypeHandler(Class<E> enumClass) {
        this.enumClass = enumClass;
    }

    public void setParameter(ParameterSetter ps, Object o) throws SQLException {
        if (enumClass.isInstance(o)) {
            ps.setString((enumClass.cast(o)).name().toUpperCase());
        } else {
            throw new SQLException("Excpected ParameterType object than: " + o);
        }
    }

    public Object getResult(ResultGetter rs) throws SQLException {
        try {
            return Enum.valueOf(enumClass, rs.getString());
        } catch (IllegalArgumentException e) {
            throw new SQLException("Unknown parameter type: " + rs.getString(), e);
        }
    }
}

您可以按照以下方式使用它:
public class XEnumTypeHandler extends CommonEnumTypeHandler<X> {
    public XEnumTypeHandler() {
        super(X.class);
    }
}

我仍然不确定OP想要什么,但我会点赞这个帖子。我相信你是正确的 =) - polygenelubricants
1
setParameter 中,将 isAssignableFrom 更改为 enumClass.isInstance( o ) - Alexander Pogrebnyak
@poly:他需要一个通用的枚举<-->字符串转换器,用于对象关系映射器。Java枚举类型在JDBC中不受支持。@Alex:已更新! - BalusC
1
@BalusC:无论是什么技能让你知道这个,我都想学习。 - polygenelubricants
1
@poly: 学习JDBC和DAO,并尝试重构所有内容,仍然保持跨数据库兼容性。换句话说:重新发明Hibernate/iBATIS/JPA ;) - BalusC
1
@polygenelubricants。与客户进行长时间的可用性会议。我得出了类似的解决方案,但Balus比我快几秒钟。 - Alexander Pogrebnyak

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