I am designing a simple Data Access Object for my Java application. I have a few classes (records) that represents a single row in tables like User
and Fruit
.
I would like to have a single method for getting all records of a specific type.
For the moment I have it like this:
public List<User> getAllUsers() {
...
}
public List<Fruit> getAllFruits() {
...
}
....
But I would like to have a single polymorphic method like this (wrong):
public List<T> getAllRecords(Class<T> type) {
if(type instanceof User) {
// Use JDBC and SQL SELECT * FROM user
} else if(type instanceof Fruit) {
// Use JDBC and SQL SELECT * FROM fruit
}
return collection;
}
Example for uses:
List<Fruit> fruits = myDataAccessObject.getAllRecrods(Fruit.class);
List<User> users = myDataAccessObject.getAllRecords(User.class);
How can I do this in Java?
It looks like you want to adapt what Josh Bloch calls a Typesafe Heterogenous Container pattern: you are passing a type token Class<T>
, and you want back a List<T>
.
Plain old THC can map a Class<T>
to a T
in a typesafe manner, but since you actually want a List<T>
instead, then you want to use what Neal Gafter calls the super type tokens.
The following snippet is adapted from Crazy Bob Lee's code posted in Neal Gafter's blog:
public abstract class TypeReference<T> {
private final Type type;
protected TypeReference() {
Type superclass = getClass().getGenericSuperclass();
if (superclass instanceof Class<?>) {
throw new RuntimeException("Missing type parameter.");
}
this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
}
public Type getType() {
return this.type;
}
}
Now you can create a super type token like these:
TypeReference<String> stringTypeRef =
new TypeReference<String>(){};
TypeReference<Integer> integerTypeRef =
new TypeReference<Integer>(){};
TypeReference<List<Boolean>> listBoolTypeRef =
new TypeReference<List<Boolean>>(){};
Essentially you pass a TypeReference<T>
instead of a Class<T>
. The difference is that there is no List<String>.class
, but you can make a TypeReference<List<String>>
.
So now we can make our container as follows (the following is adapted from Josh Bloch's original code):
public class Favorites {
private Map<Type, Object> favorites =
new HashMap<Type, Object>();
public <T> void setFavorite(TypeReference<T> ref, T thing) {
favorites.put(ref.getType(), thing);
}
public <T> T getFavorite(TypeReference<T> ref) {
@SuppressWarnings("unchecked")
T ret = (T) favorites.get(ref.getType());
return ret;
}
}
Now we can put the two together:
Favorites f = new Favorites();
f.setFavorite(stringTypeRef, "Java");
f.setFavorite(integerTypeRef, 42);
f.setFavorite(listBoolTypeRef, Arrays.asList(true, true));
String s = f.getFavorite(stringTypeRef);
int i = f.getFavorite(integerTypeRef);
List<Boolean> list = f.getFavorite(listBoolTypeRef);
System.out.println(s); // "Java"
System.out.println(i); // "42"
System.out.println(list); // "[true, true]"
Neal Gafter argued in his blog that with some more bells and whistles, TypeReference
for super type tokens will make a worthy inclusion in the JDK.