来自抽象类的 Java 通用子实例

标签 java generics abstract-class

我有一个抽象类、一些子类和抽象类的通用 DAO。

public abstract class Person { ... }
public class User extends Person { ... }
public class Client extends Person { ... }
public PersonDao <T extends Person> { ... }

我想在 PersonDao 中创建一个 select 方法来返回 Person 类的子对象。

public T select(int id) throws SQLException {
    String sql = "SELECT * FROM person WHERE per_id=?";
    PreparedStatement ps = con.preparedStatement(sql);
    ps.setInt(1, id);
    ResultSet rs = ps.executeQuery();
    T t = null;

    if(rs.next()) {
        t = new T(); // Error line

        t.setId(rs.getInt("per_id"));
        t.setName(rs.getString("per_name"));
    }

    return t;
}

如何实例化通用子类并返回子对象?

最佳答案

new T() 不允许,因为 type erasure在 java 。无论如何,泛型的全部目的是确保编译时代码中的类型安全,而数据访问类不能确保这一点,因为数据库中的条目可能对应于 UserClient。换句话说,SQL 语句和子类之间没有映射(请注意,这通常由 Hibernate 等 ORM 框架处理)。解决方案是在代码中提供额外的参数来准确指定结果类型。通常,Person 的每个子类都应该有自己的 DAO 实现(即 UserDaoClientDao),而 PersonDao 方法应该是抽象的,例如:

public abstract class PersonDao<T extends Person> { 

     abstract T select(int id);

     ...
}

public class UserDao extends PersonDao<User> {

    public User select(int id) throws SQLException {
        // Assuming there is a type column to differentiate between types of Person
        String sql = "SELECT * FROM person WHERE per_id=? and type=?";
        PreparedStatement ps = con.preparedStatement(sql);
        ps.setInt(1, id);
        ps.setString(2, User.class.getName());
        ResultSet rs = ps.executeQuery();
        User user = null;

        if(rs.next()) {
            user = new User();

            user.setId(rs.getInt("per_id"));
            user.setName(rs.getString("per_name"));
        }

        return user;
    }

    ...
}

如果代码可以在父 PersonDao 中共享,因为它在两个实现中是相同的(在这种情况下,我不认为首先使用 PersonDao 泛型有什么意义,您可以让它返回一个 Person 而不是 T),那么您可以在构造函数中传递该类:

public class PersonDao<T extends Person> { 

     private Class<T> type;

     public PersonDao(Class<T> type) {
         this.type = type;
     }

     public T select(int id) {
        String sql = "SELECT * FROM person WHERE per_id=? and type=?";
        PreparedStatement ps = con.preparedStatement(sql);
        ps.setInt(1, id);
        ps.setString(2, type.getName());
        ResultSet rs = ps.executeQuery();
        T t = null;

        if(rs.next()) {
            t = type.newInstance();

            t.setId(rs.getInt("per_id"));
            t.setName(rs.getString("per_name"));
        }

        return t;
     }

     ...
}

旁注:您的代码片段没有显示 JDBC 资源是如何关闭的。确保在您的真实代码中执行此操作。

关于来自抽象类的 Java 通用子实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42423948/

相关文章:

java - 如何让两个相同的读+写并发事务相互等待?

java - Spring boot 2.0.5.RELEASE 应用程序未部署在 JBoss 6.4.0 中

Java:混合泛型和 VarArgs

Java 未经检查的重写返回类型

c++ - STL 中的抽象 map 和 vector

java - 基于访问器方法强制执行严格继承

java - 为什么 Spring 的 @Transactional 不适用于 protected 方法?

Java 流 : Organize a collection into a map and select smallest key

generics - Grails 类型检查/CompileStatic/GrailsCompileStatic

斯卡拉 : Collect with generics