Reputation: 1564
I just implement a MapBuilder
to build map easy,
But when i try to get an instance of HashMap.class,I suddenly found that I can't use HashMap.class to get such an instance.
It's illegal!
So can anybody tell me why and how to solve this problem?
The MapBuilder is follow:
import java.util.Map;
public abstract class MapBuilder {
public static <K, V, T extends Map<K, V>> InnerMapBuilder<T, K, V> start(
Class<T> clazz) {
return new InnerMapBuilder<>(clazz);
}
public static class InnerMapBuilder<T extends Map<K, V>, K, V> {
private T target;
public InnerMapBuilder(Class<T> clazz) {
try {
target = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public InnerMapBuilder<T, K, V> put(K key, V val) {
target.put(key, val);
return this;
}
public T get() {
return target;
}
}
}
And the test code is below:
public static void main(String[] args) {
HashMap<String, String> v = start(HashMap<String,String>.class).put("a", "b").get();
System.out.println(v);
}
Upvotes: 3
Views: 444
Reputation: 81074
It's impossible to obtain a parameterized class type variable for a generic type, as Reimeus has said. So you have three choices.
First, you can live with the unchecked cast:
Class<? extends Map<String, Integer>> clazz =
(Class<? extends Map<String, Integer>>) HashMap.class;
Second, you can reify the parameters for a class by extending it (in this example, using an anonymous inner class):
Class<? extends Map<String, Integer>> clazz =
new HashMap<String, Integer>() {}.getClass();
Or third, and best, just take the Map
instance instead of a class in start()
. You're not saving the user any work by taking the Class
rather than an instance of Map
, and the first thing you do is create an instance of it.
By passing it in, the user can even tweak the settings of the map (e.g. for a HashMap
, set the load factor, for TreeMap
, specify the Comparator
) so it's a better alternative anyway. If you need to, you can assert that it's empty when it's passed in.
If for some reason you really need a factory, don't use Class
: it doesn't work well as a factory, because the only way you can customize the instance that Class
creates is by subclassing the class and providing a new no-arg constructor. Just create an interface Factory<T>
that has a method T create()
and then accept a Factory<? extends Map<K, V>
.
Upvotes: 1
Reputation: 159754
First since start
takes a class you would have to pass it an unparameterized class such as HashMap.class
. Second, as you're returning a generic type Map
, you would have to make your local type match also, so to use:
Map<String, String> v = start(HashMap.class).put("a", "b").get();
Upvotes: 0