简体   繁体   中英

How to instance generic parameterized class

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);
}

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 (eg 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> Factory<? extends Map<K, V> .

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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM