Is there a way to avoid the constructor passing the Class?
Consider this HashMap extention (generates an instance of the V class when calling "get" if it's null)
public class HashMapSafe<K, V> extends HashMap<K, V> implements Map<K, V>{
开发者_如何学Go private Class<V> dataType;
public HashMapSafe(Class<V> clazz){
dataType = clazz;
}
@SuppressWarnings("unchecked")
@Override
public V get(Object key) {
if(!containsKey(key)){
try {
put((K)key, dataType.newInstance());
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return super.get(key);
}
}
The usage of it is something like this
Map<String,Section> sections = new HashMapSafe<String,Section>(Section.class);
sections.get(sectionName); //always returns a Section instance, existing or new
It seems to me redundant a little to supply "Section" twice, once as a generic type, and also supply it's class. I assume it's impossible, but is there to implement HashMapSafe, (keeping the same functionality) so it can be used like this?
Map<String,Section> sections = new HashMapSafe<String,Section>();
Or like this?:
Map<String,Section> sections = new HashMapSafe<String>(Section.class);
You cannot improve the constructor usage due to type erasure as others have already pointed out, but you should be able to improve verbosity by using a static factory method instead of a constructor...
I am not in front of compiler and I can never get method type parameters right on first try, but it will go something like this...
public static <K,V> Map<K,V> create( Class<V> cl )
{
return new HashMapSafe<K,V>(cl);
}
...
Map<String,Section> sections = HashMapSafe.create(Section.class);
No, there isn't. Java doesn't have Reified generics.
Guava has however a nice solution in flavor of MapMaker#makeComputingMap()
Map<String, Integer> numbers = new MapMaker().makeComputingMap(new Function<String, Integer>() {
public Integer apply(String key) {
return 0;
}
});
Which sets and returns 0
instead of null
when the key is not present (and it is threadsafe, as opposed to your solution).
Neither are possible. The first would require being able to do something like new V()
and that can't be done. The second would require being able to set the V
type at runtime because it's passed in the constructor, which also can't be done. Keep in mind that generics are only used a compile time and they're erased for runtime.
I found this article interesting: Reflecting generics
in short using:
public abstract class AbstractUserType<T> implements UserType {
...
public Class returnedClass {
ParameterizedType parameterizedType =
(ParameterizedType) getClass().getGenericSuperClass();
return (Class) parameterizedtype.getActualTypeArguments()[0];
}
...
}
Is there a way to avoid the constructor passing the Class?
In short, No.
The root cause is "type erasure". At runtime, the implementation of HashMapSafe has no implicit knowledge of the types that correspond to the type parameters. That information is erased. If you need the implementation to know what the type is, you need to pass it as a Class
object that is compatible with Class<V>
.
Map<String,Section> sections = new HashMapSafe<String,Section>();
That doesn't work because of type erasure; see above.
Map<String,Section> sections = new HashMapSafe<String>(Section.class);
That doesn't work because the Java language requires you to specify a type (or wildcard) for all of the type parameters when you use that syntax.
精彩评论