【问题标题】:Generic method returning a List while instantiating the generic Type passed in the method泛型方法在实例化方法中传递的泛型类型时返回一个列表
【发布时间】:2018-04-30 06:48:57
【问题描述】:

我已经苦苦挣扎了很长一段时间,并查看了一些资源,试图理解我如何使用泛型来获得我想要实现的目标,但我无法找到一个提供我想要实现的示例需要。

一些示例包括使用 Supplier 接口或建议使用泛型类然后实例化它。

我想要实现的是指定我希望方法返回的类型,同时在方法中实例化相同的类型并将其添加到列表中,然后返回该列表。

提前致谢。希望有任何帮助

public List<Doctor> queryDoctor() {
    Statement statement = null;
    ResultSet results;

    try {
        statement = connection.createStatement();
        results = statement.executeQuery("SELECT * FROM " + TABLE_DOCTOR);

        List<Doctor> doctors = new ArrayList<>();
        while (results.next()) {
            Doctor doctor = new Doctor();
            doctor.setId(results.getInt(1));
            doctor.setUserName(results.getString(2));
            doctor.setPassword(results.getString(3));
            doctor.setPassword_salt(results.getString(4));
            doctor.setRole(results.getInt(5));
            doctors.add(doctor);

        }
        return doctors;
    } catch (SQLException e) {
        System.out.println("Query failed: " + e.getMessage());
        return null;
    } finally {
        try {
            if (statement != null) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

【问题讨论】:

  • 您需要传入Function&lt;ResultSet, T&gt;,并让该函数创建T(例如Doctor)。
  • 我个人更喜欢用这样的泛型方法编写一个抽象类,为每一行调用抽象方法(buildInstance)并为Doctor实现一个类来创建实例并以正确的顺序获取参数.如果替代方案很有趣,我会提供答案。
  • @ThomasFlinkow 我正在尝试填充一个表格.. 而不是为每个演员(例如医生、护士、接待员)使用不同的方法,我想用一种方法来实现它,因为所有演员都共享相同的属性,而不是在运行时

标签: java generics generic-list


【解决方案1】:

您需要传入Function&lt;ResultSet, T&gt;

<T> List<T> queryDoctor(Function<ResultSet, T> fn)

并在循环中使用它:

doctors.add(fn.apply(results));

此函数将创建适当的具体类型:

List<Doctor> doctors = queryDoctor(results -> {
       Doctor doctor = new Doctor();
        doctor.setId(results.getInt(1));
        doctor.setUserName(results.getString(2));
        doctor.setPassword(results.getString(3));
        doctor.setPassword_salt(results.getString(4));
        doctor.setRole(results.getInt(5));
        return doctor;
});

List<Nurse> nurses = queryDoctor(results -> {
       Nurse nurse = new Nurse();
       // ...
       return nurse;
});

【讨论】:

  • 我不确定,因为我想实例化相同的对象类型。如果我需要返回护士、医生、患者列表,那么它会实例化护士、医生或患者在方法中。请解释
  • 然后传入results -&gt; { Nurse nurse = new Nurse; /* do stuff to populate nurse */ return nurse }作为函数。
【解决方案2】:

听起来您正在尝试自己的简单 ORM 解决方案,其中您提供带有 SQL 字符串和类型的方法,该方法加载结果然后实例化类型,通过 setter 将结果集映射到对象的字段。

如果是这样,你就是在重新发明轮子。

完整的解决方案是使用诸如 Hibernate 或 Top Link 之类的 ORM 工具,但它们可能会在您走路之前运行。如果您更喜欢小步骤,请考虑使用 Spring 的 Jdbc 模板来处理大量样板代码。

更新

好的,我问你是否正在寻找一种方法来获取结果集并将其映射到任意类,你说是的。这应该做这样的事情:

public <T> List<T> map(ResultSet rs, Class<T> clazz) throws Exception {
    //// Build a list of columns that are in the resultset
    List<String> columns = new LinkedList<>();
    ResultSetMetaData metaData = rs.getMetaData();
    for(int i=0; i < metaData.getColumnCount(); i++) {
        columns.add(metaData.getCatalogName(i));
    }

    //// Create a list to hold all the beans we are going to create from the resultset 
    List<T> rows = new LinkedList<>();

    //// Iterate over each row in the resultset, creating beans for each row and setting the values 
    while(rs.next()) {
        //// Create a new instance of the class passed in (must have a no-args constructor) 
        T row = clazz.newInstance();

        //// Now for the magic. This bit is very noddy and ORM packages will do this much better 
        for(String col : columns) {
            //// For each column that is in the resultset, find the setter in the bean class 
            String columnValue = rs.getString(col);
            String mutator = "set"+capitalizeFirstCharacter(col);
            Method m = clazz.getMethod(mutator, String.class);
            //// Invoke the setter, passing in the value from the resultset 
            m.invoke(row, columnValue);
        }

        rows.add(row);
    }

    return rows;
}

private static String capitalizeFirstCharacter(String s) {
    return s.substring(0, 1).toUpperCase() + s.substring(1);
}

请注意,此代码不处理异常,仅适用于填充了字符串的结果集,并且仅处理具有要填充的字符串字段的 bean。一旦你开始尝试编写更复杂的代码来处理更复杂的类型和反射......你正在重新发明 ORM。尽量不要那样做。

【讨论】:

  • 使用 Hibernate 只从一张表中获取 POJO 会非常繁重。配置、学习时间……当一个纯 JDBC 解决方案可以轻松完成时增加的复杂性似乎没有必要。解决方案,但不是通用解决方案。
  • @Ramsay 是的,但我听说过 hibernate 和 spring,但这是我的大学项目,我没有使用过你提到的技术
  • 那么您是否尝试创建一种通用方法,可以将 SQL 转换为对象集合,正如我所建议的那样?
  • @RamsayDomloge 是的
猜你喜欢
  • 2016-12-24
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
  • 1970-01-01
  • 2013-07-17
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
相关资源
最近更新 更多