How to create a generic typesafe HashMap by class type? [duplicate]

a 夏天 提交于 2020-06-14 04:18:45

问题


I'd like to create a HashMap that maps specific class types to one single specific new object.

Later I want to pass the class type and get the reference to that specific object. Simple example:

Map<Class<?>, ?> values = new HashMap<>();

public <T> t get(Class<T> type) {
    return values.get(type);
}


//pet and car do not share any interface or parent class
class Pet;
class Car;

//error: not applicable for arguments
values.put(Pet.class, new Pet());
values.put(Car.class, new Car());

usage:

values.get(Pet.class);

How can I create such a generic hashmap and lookup function accordingly?


回答1:


You need to store some type of object, if you want to be able to put anything else as Object in the map, so start off with this:

Map<Class<?>, Object> values = new HashMap<>();

This has to be done this way, because ? is not a concrete object, but Object is, for the type the map stores.

So the following snippet works, without warnings:

Map<Class<?>, Object> values = new HashMap<>();
values.put(Pet.class, new Pet());
values.put(Car.class, new Car());

Now the trick is to get objects, we do it as follows:

@SuppressWarnings("unchecked")
private <T> T get(Class<T> clazz) {
    return (T)values.get(clazz);
}

Now your goal is to ensure that the map contains pairs that provide no errors on runtime. If you put a new Car() instance with Car.class , then you are going to get errors.

So the following example code:

    values = new HashMap<>();
    values.put(Pet.class, new Pet());
    values.put(Car.class, new Car());

    System.out.println("get(Pet.class).getClass() = " + get(Pet.class).getClass());
    System.out.println("get(Car.class).getClass() = " + get(Car.class).getClass());

will print:

get(Pet.class).getClass() = class testproject8.Pet
get(Car.class).getClass() = class testproject8.Car



回答2:


I think you are after a heterogeneous container. There is no easy way to create such a container in Java without any explicit casts.

I think the easiest way is to put your map in a class -

public class Container {
    private Map<Class<?>, Object> values = new HashMap<>();

    public <T> T put(Class<T> klass, T obj) {
        return klass.cast(values.put(klass, obj));
    }

    public <T> T get(Class<T> klass) {
        return klass.cast(values.get(klass));
    }
}

And use the class as follows -

Container c = new Container();
c.put(Pet.class, new Pet());
c.put(Car.class, new Car());
Pet p = c.get(Pet.class);



回答3:


You can use Object as the value type:

Map<Class<?>, Object> values = new HashMap<>();

You don't know the type of the values anyway.

Then, if you want to be sure that the object inserted via put is of the class that serves as key, you may create safePut() the following way:

public <T> T safePut(Class<T> key, T value) {
    return (T) values.put(key, value);
}


来源:https://stackoverflow.com/questions/22941715/how-to-create-a-generic-typesafe-hashmap-by-class-type

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!