3

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

2 Answers 2

1

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>.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for good answer, I'll take some time to think about your words.
0

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

2 Comments

thank you , but this should not be type safe and will get some generic type warnings
Yes but you can't define a parameterized class type variable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.