1

Here is some code which does not compile. I would like to hold arbitrary pairs of Class<?> (key) and Map<Long, ObjectDescriptors<?>> (values) inside the typeMap;

From other methods I want to use one of the value maps with a concrete type, but the algorithms should be generic. I wrote the getIdentityMap() method to deliver a map with a concrete but generic type.

How must I modify the code to compile while being generic?

Thanks in advance.

    private final Map<Class<?>, Map<Long, ObjectDescriptor<?>>> typeMap = new HashMap<Class<?>, Map<Long,ObjectDescriptor<?>>();

private <T> Map<Long, ObjectDescriptor<T>> getIdentityMap(Class<T> type) {
    Map<Long, ObjectDescriptor<T>> identityMap = typeMap.get(type);
    if (identityMap == null) {
        identityMap = new HashMap<Long, ObjectDescriptor<T>>();
        typeMap.put(type, identityMap);
    }
    return identityMap;
}

EDIT: Here are the compiler errors:

  • In line: Map<Long, ObjectDescriptor<OBJECT_TYPE>> identityMap = typeMap.get(type);
    • compiler error: Type mismatch: cannot convert from Map<Long,ObjectDescriptor<Object>> to Map<Long,ObjectDescriptor<OBJECT_TYPE>>
  • In line: typeMap.put(type, identityMap);
    • compiler error: The method put(Class<?>, Map<Long,ObjectDescriptor<Object>>) in the type Map<Class<?>,Map<Long,ObjectDescriptor<Object>>> is not applicable for the arguments (Class<OBJECT_TYPE>, Map<Long,ObjectDescriptor<OBJECT_TYPE>>)

EDIT: edited the generic types of the map attribute to ?

anonymous
  • 153
  • 1
  • 1
  • 5
  • Please don't use , but just instead. It's confusing to read when you don't respect the convention. – toto2 Jul 16 '11 at 12:45
  • Please also include compilation errors. – toto2 Jul 16 '11 at 12:48
  • The basic problem is that you cannot instantiate a generics class with a type variable: `new Something();` is impossible. This is due to the way generics are implemented in Java (erasure). For a workaround see [this post](http://stackoverflow.com/questions/1090458/instantiating-a-generic-class-in-java). There are probably better posts closer to your problem, but I have to run now... – toto2 Jul 16 '11 at 13:05
  • @toto: Why is `new Something` impossible? `List t = new ArrayList()` is very much capable of compiling. Of course if you say `new T()` then yes, that's impossible. – Sanjay T. Sharma Jul 16 '11 at 14:04
  • @Sanjay thanks. Quite tricky: `new ArrayList>()` is invalid, but `new ArrayList>()` is permissible. – toto2 Jul 17 '11 at 16:48
  • @anonymous Please mostly ignore this comment... it's just rambling. I don't quite know what you are doing, but I was thinking that instead of having those external `ObjectDescriptor`s, you might integrate whatever functionality they provide directly into the `T`'s by implementing some interface. – toto2 Jul 17 '11 at 17:05
  • @toto: Generics are tricky beasts indeed. :-) If you need more in-depth analysis, have a look at the [Generics FAQ](http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html); quite a nice resource IMO. – Sanjay T. Sharma Jul 18 '11 at 05:21

2 Answers2

1

You cannot create generic instance with "T". Try using the "?"

 private final Map<Class<?>, Map<Long, ObjectDescriptor<?>>> typeMap = new HashMap<Class<?>, Map<Long,ObjectDescriptor<?>>>();


private <T> Map<Long, ObjectDescriptor<?>> getIdentityMap(Class<?> type) {
    Map<Long, ObjectDescriptor<?>> identityMap = typeMap.get(type);
    if (identityMap == null) {
        identityMap = new HashMap<Long, ObjectDescriptor<?>>();
        typeMap.put(type, identityMap);
    }
    return identityMap;
}

As mentioned in the comments, this is because of Java erasure to maintain compatibility with older classes which did not use generics. The compiler removes the type information from parameters and arguments.

kensen john
  • 5,439
  • 5
  • 28
  • 36
  • You are pretty much ignoring the type parameter? – Sanjay T. Sharma Jul 16 '11 at 14:05
  • @Sanjay T. Sharma the question was primarily how to remove compilation errors, so I just assumed, rest of the stuff would be taken care by the developer. But I see that you have included everything, so leaving my answer as is. – kensen john Jul 16 '11 at 14:10
1

You can do it if you are willing to re-design a bit and are OK with a single unchecked cast (which should never fail IMO).

public class TmpTest {

    public static void main(final String[] args) {
        ClassDescriptorMap m = new ClassDescriptorMap();

        LongDescriptorMap<String> identityMap = m.getIdentityMap(String.class);
        identityMap.put(1L, Arrays.asList("hi"));
        System.out.println(identityMap);
        identityMap = m.getIdentityMap(String.class);
        identityMap.put(2L, Arrays.asList("hello"));
        System.out.println(identityMap);

        LongDescriptorMap<Object> identityMap2 = m.getIdentityMap(Object.class);
        System.out.println(identityMap2);
    }

}

class ClassDescriptorMap {

     private final Map<Class<?>, LongDescriptorMap<?>> typeMap = new HashMap<Class<?>, LongDescriptorMap<?>>();

     public <T> LongDescriptorMap<T> getIdentityMap(Class<T> type) {
         @SuppressWarnings("unchecked")
        LongDescriptorMap<T> identityMap = LongDescriptorMap.class.cast(typeMap.get(type));
         if (identityMap == null) {
             identityMap = new LongDescriptorMap<T>();
             typeMap.put(type, identityMap);
         }
         return identityMap;
     }

     @Override
     public String toString() {
         return typeMap.toString();
     }
}

class LongDescriptorMap<T> {

    private Map<Long, List<T>> map = new HashMap<Long, List<T>>();

    public List<T> get(Object key) {
        return map.get(key);
    }

    public List<T> put(Long key, List<T> value) {
        return map.put(key, value);
    }

    @Override
    public String toString() {
        return map.toString();
    }

}
Sanjay T. Sharma
  • 22,857
  • 4
  • 59
  • 71
  • Thx for your work, but this isn't what I am looking for. This solutions seems to be doing something different. – anonymous Jul 16 '11 at 14:52
  • @anonymous: Of course, this was an example; just replace `List` with `ObjectDescriptor` in `LongDescriptorMap` and you should be good to go, no? – Sanjay T. Sharma Jul 16 '11 at 15:01
  • OK, the most important thing you did is: `MyClass.class.cast()`. Now I did: `Map> identityMap = Map.class.cast(typeMap.get(type));` and the other way round. Seems to work at the first look. I have to test it more completely. I just don't understand why a simple cast does not compile? Is this realy because of type erasure? – anonymous Jul 16 '11 at 15:38
  • Well, it doesn't compile because you don't cast it and the compiler has no way of knowing for sure that the conversion you are performing would fail at runtime or not. – Sanjay T. Sharma Jul 16 '11 at 16:11