Reputation: 69339
Whilst playing around with solutions for this question, I came up with the following code, which has some compiler warnings. One warning is:
Type safety: The expression of type Test.EntityCollection needs unchecked conversion to conform to Test.EntityCollection<Test.Entity>
I don't entirely understand why this warning appears. By passing in a Class<M>
type and declaring the method returns EntityCollection<M>
, why am I not doing enough to convince the (Java 7) compiler that the correct type is being returned?
static class Entity {
}
static class EntityCollection<E extends Entity> {
private EntityCollection(HashMap<?, E> map) {
}
public static <T extends HashMap<?, M>, M extends Entity> EntityCollection<M> getInstance(
Class<T> mapType, Class<M> entityType)
throws ReflectiveOperationException {
T map = mapType.getConstructor().newInstance();
return new EntityCollection<M>(map);
}
}
public static void main(String[] args) throws Exception {
// both compiler warnings are on the line below:
EntityCollection<Entity> collection = EntityCollection.getInstance(
LinkedHashMap.class, Entity.class);
}
Bonus points if anyone can improve the code to avoid warnings entirely. I've been staring at it for a while and haven't dreamt up any ways to lessen the warnings.
Upvotes: 7
Views: 138
Reputation: 30528
The problem is that getInstance
is a generic method but you don't pass generic type parameters to it. You can get around it by passing them like this:
public static void main(String[] args) throws Exception {
EntityCollection<Entity> collection = EntityCollection.<LinkedHashMap, Entity>getInstance(
LinkedHashMap.class, Entity.class);
}
You will still have to deal a rawtypes warning because LinkedHashMap
is a generic type. This is problematic in your case since there is a wildcard in the key type.
You face several problems here:
You can't pass parameterized class objects like this: LinkedHashMap<Object, Entity>.class
so you pretty much stuck with the rawtypes warning.
Upvotes: 3
Reputation: 1858
The problem there is T. You are adding a constraint to your method saying that T should extends HashMap<?, M>
. However, the way you are later referencing to T is like a generic parameter of the type Class (Class<T>)
. LinkedHashMap.class
is of type Class<LinkedHashMap>
not Class<LinkedHashmap<?, Entity>>
(which is what you needed)
A Class object always references a non-parameterized type, and that makes sense. Because the generic binding exists in compile-time, and you are going to use that Class to dynamically reflect the state and behaviour of an instance in runtime. Long story short, you can use a Class<HashMap>
to build a new instance, not bounded to any type.
So, I guess what you need to do to your code to modify that constraint in the way it looks like:
public static <T extends HashMap, M extends Entity> EntityCollection<M> getInstance(
Class<T> mapType, Class<M> entityType)
throws ReflectiveOperationException {
T map = mapType.getConstructor().newInstance();
return new EntityCollection<M>(map);
}
Upvotes: 1