Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

generics

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.

like image 332
Sled Avatar asked Jul 22 '11 19:07

Sled


2 Answers

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

like image 185
fyr Avatar answered Oct 21 '22 16:10

fyr


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?

like image 3
Jay Avatar answered Oct 21 '22 17:10

Jay