8

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?

membersound
  • 81,582
  • 193
  • 585
  • 1,120

3 Answers3

6

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
skiwi
  • 66,971
  • 31
  • 131
  • 216
4

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);
Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
1

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);
}
Joffrey
  • 32,348
  • 6
  • 68
  • 100