I am trying to make my own repository for my database to learn, so I am trying something like this:
@Override
public <T extends DatabaseObject> List<T> getList() {
Cursor cursor = getCursor(somehowGetClassOfT(), null, null); //how to do this?
//excess code removed, rest of function not relevant to question
return list;
}
protected <T extends DatabaseObject> Cursor getCursor(Class<T> clazz, String selection, String[] selectionArgs) {
DatabaseObject databaseObject = instantiateFromT(clazz); //how to do this?
String tableName = databaseObject.getTableName();
String[] projection = databaseObject.getProjection();
String sortOrder = databaseObject.getDefaultOrderColumn() + " " + DEFAULT_SORT_ORDER;
Cursor cursor = database.query(
tableName,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
return cursor;
}
In other words I have many classes that extend DatabaseObject, and I want to be able to construct a Cursor for them dynamically.
I've defined basic methods in a DatabaseObject such as getting the table name, String array of column names, etc, but since I cannot override static methods via interface (for things like table name), I have to instantiate a blank object so I can get the table name with a getter.
However, I am not sure how to implement:
somehowGetClassOfT()
. Whatever T
is, I want to pass the class for it into the Cursor function.
instantiateFromT(clazz)
. Given some class, call the constructor so I can get access to that object's table/projection/sort fields.
Or is all of this possible by using "reflection" that I've been hearing about?
Generics are not reified at run-time. Since the information is not present, you have two solutions :
Here an example with a private field T and a constructor, by starting from your code :
public abstract class MyRepository<T extends DatabaseObject> {
private Class<T> type;
public MyRepository(Class<T> type) {
this.type = type;
}
public <T> List<T> getList() {
Cursor cursor = getCursor(null, null); // how to do this?
// excess code removed, rest of function not relevant to question
return null;
}
protected <T extends DatabaseObject> Cursor getCursor(String selection, String[] selectionArgs) {
DatabaseObject databaseObject = instantiateFromType(); // how to do this?
String tableName = databaseObject.getTableName();
String[] projection = databaseObject.getProjection();
String sortOrder = databaseObject.getDefaultOrderColumn() + " " + DEFAULT_SORT_ORDER;
Cursor cursor = database.query(
tableName,
projection,
selection,
selectionArgs,
null,
null,
sortOrder);
return cursor;
}
private DatabaseObject instantiateFromType() {
try {
T interfaceType = (T) type.newInstance();
return interfaceType;
}
catch (Exception e) {
// TODO to handle
}
}
}
Here your DogRepository :
public class DogRepository extends MyRepository<Dog> {
public DogRepository() {
super(Dog.class);
}
}
with a Dog which implements DatabaseObject:
public class Dog implements DatabaseObject{
//todo
}
To answer to your comment.
The line DogRepository(Class type), is it necessary to pass in the class as an argument?
Not is is not needed (sorry I bad read your comment)).
And to go further, as you may notice, constructors for concrete repositories are boiler plate code.
If you use reflection to resolve the class behind the generic used in DogRepository, you don't need to define a custom constructor any longer for it.
For example to retrieve the type used in the repository, Spring does the job with its own library using JDK reflection mechanisms with something like that :
Map<TypeVariable, Type> source = GenericTypeResolver.getTypeVariableMap(DogRepository.class);
Spring has its library because it uses much reflection.
But in your case, you can also do the job without using a lib but with your own utility class. Here a sample to resolve the generic type from a repository instance.
DogRepository dogRepository = new DogRepository();
Class<?> typeClass = (Class<?>) ((ParameterizedType) dogRepository.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
System.out.println("typeClass=" + typeClass );
With reflection to resolve the effective generic type in MyRepository
constructor :
public abstract class MyRepository<T extends DatabaseObject> {
private Class<T> type;
public MyRepository() {
type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
...
}
repository concrete classes don't need custom constructor any longer :
public class DogRepository extends MyRepository<Dog> {
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With