In Java is it possible to create a type-safe Map of classes to instances of their class?

12,357

Solution 1

As I understand you, you're saying that after you create this map, you want to populate it with something like ...

f.put(String.class, "Hello");
f.put(Integer.class, new Integer(42));
f.put(x.getClass(), x);

etc. Right?

In that case, I think the answer is no, you cannot do that with generics. Generics say that for a given instance of the class -- the map in this case -- you are specifying the types that are applicable. So if you say new HashMap<String,Integer>;, you are saying that all operations against this map will use a key that is a string and a value that is an integer. But that's not what you want to do in this case. You want to be able to put any sort of object into the class, and then constrain the acceptable types for the key based on the type of the object. That's not how generics work. They're not a relationship between each other, they're a constant for any given instance.

You could, of course, create such a map as new HashMap<Class,Object>;. This wouldn't force the class to be the class of the corresponding object, but it would allow you to enter such values.

Besides that, I think you'd need a wrapper. Should I point out that the wrapper's put would only need one parameter, as it could presumably determine the class of the parameter by doing getClass() on it, there'd be no need to tell it?

Solution 2

You mean something like this ?

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

  public <T> void setFavorite(Class<T> klass, T thing) {
    favorites.put(klass, thing);
  }

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

  public static void main(String[] args) {
    Favorites f = new Favorites();
    f.setFavorite(String.class, "Java");
    f.setFavorite(Integer.class, 0xcafebabe);
    String s = f.getFavorite(String.class);
    int i = f.getFavorite(Integer.class);
  }
}

see as reference: Java map with values limited by key's type parameter

Solution 3

In your example, T would have to be different for each key/value, whereas with generics, T must be the same for each key/value. So no, this is not possible using generics. (but of course the implementation is certainly possbile with casting and/or not using generics)

Share:
12,357

Related videos on Youtube

Sled
Author by

Sled

A Java Developer who has worked at places ranging from a Fortune 100 company to a small start-up. Using Java for over 20 years.

Updated on June 04, 2022

Comments

  • Sled
    Sled almost 2 years

    I would like to create a map that uses a class as a key to return an instance of that class. Something like:

    <T> Map< Class<T>, T > instanceMap = new HashMap< Class<T>, T > ();
    instanceMap.put( Boolean.class, Boolean.TRUE );
    instanceMap.put( String.class, "asdf" );   
    instanceMap.put( Integer.class, 11 );
    
    Boolean b = instanceMap.get( Boolean.class );
    Integer i = instanceMap.get( Integer.class );
    String s  = instanceMap.get( String.class  );
    

    Is this possible? I have a feeling that no it is not because I cannot indicate that T is meant to be a generic type rather than a class named "T". It is feels somehow like "higher-order generics".

    EDIT: I know I could try to extend Map and implement my own wrapper etc, but I am specifically asking about doing this just using using Java's generic support. I am more interested in the idea rather than this particular case.

    EDIT 2: As pointed out below, this is a duplicate (or more specifically a subcase) of the question: Java map with values limited by key's type parameter . Had I been able to find wording as eloquent as that, I would have likely found that answer and not posted this.

  • Sled
    Sled over 12 years
    I suspected as much, I just wanted to make sure that I wasn't missing something. But with generics, T must be the same for each key/value sounds like a limitation inherited from C++ rather than an limitation that Java generics needs to have; after all, in my example it is still statically verifiable.
  • Sled
    Sled over 12 years
    I knew this could be done with a wrapper, I was asking about using purely generics, but I'm assuming you answered before my edit.
  • Sled
    Sled over 11 years
    To clarify my earlier comment, I was more interested in the limitations of the generic type system and cooked up the illustrative example to help frame the question. That is why I have not accepted your much upvoted and otherwise reasonable answer (ie. it answers the particular rather than general after which I was going)