Java泛型构造函数

8
我目前有一段代码,从数据库中检索数据并创建一个User。这段代码被用于许多类中,以创建其他对象,例如NewsComments等等... 它使用了apache commons dbutils。
final ResultSetHandler<User> handler = new ResultSetHandler<User>() {

            @Override
            public User handle(ResultSet rs) throws SQLException {

                User user = null;
                if (rs.next()) {
                    user = new User();
                    user.setId(rs.getInt("id"));
                    user.setUsername(rs.getString("username"));
                    user.setPassword(rs.getString("password"));
                }
                return user;
            }
        };

        final User user = run.query(
                "SELECT id, username, password FROM users WHERE username = ? AND active = 2 LIMIT 1;", handler,
                username);

是否可以将QueryRunner包装在一个通用类中,并覆盖查询方法,使处理程序使用ResultSet实例化通用T。我会确保任何T类型都有一个接受ResultSet的构造函数。

像这样:

        public class QueryExecuter<T> extends QueryRunner {
    private ResultSetHandler<T> _handler;

    public QueryExecuter(){//The T type was for testing haha
        super();
        handler = new ResultSetHandler<T>() {

            @Override
            public T handle(ResultSet rs) throws SQLException {

                T object = null;
                if (rs.next()) {
                    object = new T(rs);
                }
                return object;
            }
        };
    }
}

我不知道您是否能够理解,但我希望能够。如果需要更多细节或更好的解释,请问我。

编辑

我认为我可以使用抽象类来替代所有不同对象都会扩展的通用类型,但似乎我无法编写抽象构造函数。我是否需要编写一个静态方法来返回对象实例,例如:

public abstract class DatabaseEntity {
    public static abstract DatabaseEntity create(ResultSet rs);//even this doesn't work...
}

你为何需要在构造函数中传递“T类型”? - yair
使用反射,您可以使用结果集调用类构造函数。 - user1190541
3个回答

8
可能吗?是的,但这是一个不好的主意。
你可以这样做:
class ResultSetHandler<T> {
  ResultSetHandler<T>(Class<T> clazz) {
    this.clazz = clazz;
  }

  public T handle(ResultSet rs) throws SQLException {
    T object = null;
    if (rs.next()) {
      object = clazz.getConstructor(ResultSet.class).newInstance(rs)
    }
    return object;
  }
}

混合域和数据库是一个不好的想法。更好的方法是定义一个抽象方法,根据结果集创建对象:
abstract class ResultSetHandler<T> {

  protected abstract T create(ResultSet rs);

  public T handle(ResultSet rs) throws SQLException {
    T object = null;
    if (rs.next()) {
      object = create(rs);
    }
    return object;
  }
}

然后在你的实现类中,你只需要提供一个create()方法,而不是自己处理结果集,例如:

h = new ResultSetHandler<Person>() {
  protected Person create(ResultSet rs) {
    return new Person(rs.getString("name"));
  }
}

如果您能提供与我相同的答案,并附上漂亮的代码示例,我会给您点赞。 - JB Nizet
你说将数据库与领域混合在一起不是一个好主意,我理解这一点,但是你会怎样从查询结果中构建这些对象呢? - David
我正在使用MVC架构,因此我的模型用于持久化并知道如何在数据库中保存和更新自身,但我使用实用程序来执行与数据库的查询。也许可以使用DAO? - David
是的,DAO层就是你最终要做的。本质上,它是一层,知道如何在两者之间进行转换。 - Reverend Gonzo

3
你可以通过传递要创建的对象的类,并使用反射调用其构造函数这样做,但我认为让POJO依赖于JDBC很糟糕的设计,因为它不仅知道它在数据库中是如何存储的,还知道用于加载它的查询中使用了哪些别名。
简而言之,用户POJO构造函数不应负责处理外部未知查询的结果集。
你可以设计一个AbstractSingleEntityHandler超类,其中只有。
if (rs.next()) {

使用抽象方法委托实体的实际创建过程,可以将代码块封装起来,但并不能带来太多好处。


+1 如果你比我快,而且恰好写下了我想要的内容 ;) - Mateusz Dymczyk
我正在尝试这样做,因为你在第一个代码块中看到的处理程序体在任何对象的每个查询中都会重复出现。我认为让我的来自数据库的对象知道如何从查询结果构建自己并直接返回对象将是很好的。 - David
POJO是什么意思? - David
POJO指的是纯Java对象。这种对象不依赖于任何环境,比如JEE容器或者在你的情况下是JDBC API。你需要将这段代码放在某个地方:可以是处理程序中,也可以是构造函数中。最合适的方式是将其放在处理程序中,而不是放在构造函数中。我不明白为什么将其放在构造函数中会避免重复。 - JB Nizet

0

我认为在Java中不可能做到这一点。你不能在泛型中创建一个T的实例。在Java中,泛型并不像C++中的模板一样,它们只是一个围绕object的语法糖,可以消除强制转换并引发编译时警告。

没有办法像C#那样约束T必须具有构造函数。

如果这确实是必要的,您最好使用反射来解析适当的类,但即使这样,您也会遇到问题,因为您不能在调用方法时知道T的运行时类 - 这些信息被剥离自Java字节码。因此,你只能通过传递类到方法中来使用反射。而且我不确定这是一个好的设计思路。


他可以像JB Nizet所说的那样,使用反射来完成它。 - Mateusz Dymczyk
绝对可以。你只需要一个 Class<T> 的引用。 - Reverend Gonzo
可以使用反射实现。但您仍需要向方法传递实际类。如果运行时不存在类信息,则无法使用T.class。 - Dervall

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