new T()
is not allowed due to type erasure in Java. Anyway, the whole purpose of generics is to ensure type safety in your code at compile time, and your data access class does not ensure that, because that entry in the database could correspond to either a User
or a Client
. In other words, there is no mapping between the SQL statement and the subclass (note that this is normally handled by ORM frameworks such as Hibernate). The solution is to provide additional parameters in the code that specifies exactly the result type. Typically, each child subclass of Person
should have its own DAO implementation (i.e. a UserDao
and a ClientDao
), while the PersonDao
method should be abstract, e.g.:
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;
}
...
}
If the code can be shared in the parent PersonDao
because it is the same in both implementation (in which case I don't see the point of having the PersonDao
generic in the first place, you could just make it return a Person
instead of T
), then you can pass the class in the constructor:
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;
}
...
}
Side note: Your code snippet does not show how JDBC resources are being closed. Make sure to do that in your real code.