Reputation: 9501
I have this general function to populate an ArrayList of objects from a database. The problem is that I'm getting a general ArrayList class back from the DB, and then creating the specific subclass of the ArrayList I need to create, and then copying from the generic ArrayList to my subclass. I want to eliminate that unnecessary step of copying from one array to the other, since the performance won't be great with hundreds of rows. How can I eliminate that step using generics?
So, to use a more specific example, I have a data class like
public class UserData {}
and then a class like
public class UserSet extends ArrayList<UserData>
and I would populate the UserSet object by using a function call like
UserSet s = selectAll("SELECT * FROM users", UserSet.class);
and my general function to query the DB and return a UserSet instance is like this.
public static <T, S extends List<T>> S selectAll(String sql, Class<S> listType, Object...args) throws Exception
{
// t = UserData.class in my example
Class<T> t = (Class<T>)((ParameterizedType)listType.getGenericSuperclass()).getActualTypeArguments()[0];
// From Apache's DBUtils project
QueryRunner run = new QueryRunner();
// AnnotatedDataRowProcessor is my class that just converts a DB row into a data object
ResultSetHandler<List<T>> h = new BeanListHandler<T>(t, new AnnotatedDataRowProcessor());
Connection conn = DB.getConnection();
try
{
// creates the new instance of my specific subclass of ArrayList
S result = listType.newInstance();
// returns the ArrayList which I then copy into result
result.addAll(run.query(conn, sql, h, args));
return result;
}
finally
{
DbUtils.close(conn);
}
}
Upvotes: 0
Views: 117
Reputation: 9501
I actually had to pass in the Class type into the constructor of AnnotatedDataRowProcessor so I wouldn't break the interface methods in this BeanListHandler.
private Class<?> type;
public <T> AnnotatedDataRowProcessor(Class<T> type)
{
this.type = type;
}
@Override
public <T> List<T> toBeanList(ResultSet rs, Class<T> type) throws SQLException
{
try
{
List<T> list = (List<T>)this.type.newInstance();
while (rs.next())
{
list.add(toBean(rs,type));
}
return list;
}
catch (IllegalAccessException ex)
{
throw new SQLException(ex.getMessage());
}
catch (InstantiationException ex)
{
throw new SQLException(ex.getMessage());
}
}
Upvotes: 0
Reputation: 11977
You can customize your BeanListHandler
, something like this:
ResultSetHandler<List<T>> h = new BeanListHandler<T>(t, new AnnotatedDataRowProcessor()) {
@Override
public List<T> handle(ResultSet rs) throws SQLException {
List<T> rows = listType.newInstance();
while (rs.next()) {
rows.add(this.handleRow(rs));
}
return rows;
}
};
You will probably need some casts to make this compile, but this is the general idea.
Then calling run.query(conn, sql, h, args)
will directly create the type you're looking for.
Upvotes: 1