-1

I have declared a Map which contains mapping of keys of type Long to a Nested Map

Nested Maps has keys of Type Class and values of Type SomeClient Basically I am trying to generate a mapping of Class type to the clients which produce the response of the Class Type.

private static final Map<Long, Map<Class<? super GenericResponse>, SomeClient<? super GenericResponse>>> someClientMap 
       = new HashMap<Long, Map<Class<? super GenericResponse>, SomeClient<? super GenericResponse>>>(); // <? super GenericResponse> should enable me to put subclasses of type GenericResponse in the Map

The getSomeClient method is a generic method which accepts the class Type as argument and returns the client which produces responses of the Class Type.(The class type can only be a subclass of GenericResponse: notice Type parameter in the method signature)

public static <T extends GenericResponse> SomeClient<T> getSomeClient(long clientId,Class<T> clazz) throws IOException {
    if (someClientMap.get(clientId) == null) {
        synchronized (someClientMap) {
            someClientMap.put(clientId,  new HashMap<Class<T>,SomeClient<T>>()); //getting error here                       
        }
    }
    return someClientMap.get(clientId);
}

The problem is that I am getting an compile time error where I am trying to put the client in the map.

The exact Error is

The method put(Long, Map<Class<? super GenericResponse>,SomeClient<? super GenericResponse>>) in the type Map<Long,Map<Class<? super GenericResponse>,SomeClient<? super GenericResponse>>> is not applicable for the arguments (long, HashMap<Class<T>,SomeClient<T>>)

I am having difficulty pointing out what exactly am I doing wrong. Please help.

Declaration for SomeClient is

public class SomeClient<T extends GenericResponse>

and for the contructor is

public SomeClient(Class<T> clazz) 
rohit.agrawal
  • 9
  • 1
  • 1
  • 4

3 Answers3

0

There are several problems in your code. First, if you want to allow subtypes you should use extends and not super:

private static final Map<Long, Map<Class<? extends GenericResponse>, SomeClient<? extends GenericResponse>>> someClientMap 
       = new HashMap<Long, Map<Class<? extends GenericResponse>, SomeClient<? extends GenericResponse>>>();

This is telling that you allow subtypes of GenericResponse as generic parameter of Class and SomeClient within the Map, but it doesn't allow you to use subtypes of Class (same for SomeClient) itself, and for all we know Class<T> (where T extends GenericResponse) is a subtype of Class<? extends GenericResponse>, as the former is a "specialization" of the the latter for a specific T.

Using that definition you have to keep the same generic parameters of the inner Map when putting it in the outer one:

someClientMap.put(clientId, new HashMap<Class<? extends GenericResponse>, SomeClient<? extends GenericResponse>>());

You can keep the put as you have it though, if you just add more extends to the initial declaration to allow subtypes of Class and SomeClient:

private static final Map<Long, Map<? extends Class<? extends GenericResponse>, ? extends SomeClient<? extends GenericResponse>>> someClientMap
        = new HashMap<Long, Map<? extends Class<? extends GenericResponse>, ? extends SomeClient<? extends GenericResponse>>>();

And then you can just do:

someClientMap.put(clientId, new HashMap<Class<T>, SomeClient<T>>());

Either way, you will not get away from ugly casts when accessing someClientMap:

return (SomeClient<T>) someClientMap.get(clientId).get(clazz);
Helder Pereira
  • 4,704
  • 2
  • 27
  • 45
  • Thanks for the answer, but I have used super instead of extends because I want to add different subclasses of the GenericType class to the same map. Please refer this question https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java for an explaination. – rohit.agrawal Aug 08 '17 at 18:39
  • @rohit.agrawal Did you even read the answers to that question? It's pretty simple: if you want to allow **subclasses** of GenericType use `? extends GenericType`; if you want to allow **superclasses** of GenericType use `? super GenericType`. – Helder Pereira Aug 08 '17 at 19:04
  • I don't just want to allow one subclass of the GenericType in the map, I want to allow multiple subtypes in the same map. Copying some parts of the answer in the link here to emphasise what I am saying. If a list is declared as `List extends Number> foo3` ; You can't add an Integer because foo3 could be pointing at a List. You can't add a Double because foo3 could be pointing at a List. You can't add a Number because foo3 could be pointing at a List. – rohit.agrawal Aug 09 '17 at 05:40
  • In that list example, if you used `List super Number>` instead, you really could happily add Integers and Doubles. The problem would be when you try to get anything, because all you would know is that it is an `Object`; you could not be sure they are Numbers, as the list can be of any superclass of Number. What you would want in that example is to avoid using wildcards all together and have a plain `List`, where you can happily add Integers and Doubles, and when you get anything at least you know it's a Number. – Helder Pereira Aug 09 '17 at 07:10
  • Still my first suggestion is what you should go with, because my `extends` is inside the classes within the map and not in the container itself, which is the case in your list example and in my second suggestion. – Helder Pereira Aug 09 '17 at 07:11
0

The error is due to the fact that Java generics are invariant, i.e. although String is a subtype of Object the following line will fail to compile:

Map<Long, Object> map = new HashMap<Long, String>();

because Map<Long, String> is not a subtype of Map<Long, Object>.

But we can get over this restriction by using bounded wildcards, i.e.

Map<Long, ? extends Object> map = new HashMap<Long, String>();

This why there is a Class<? super GenericResponse> in your code, but you stopped about half way. You should have used the same technique in the next upper level, i.e. write:

Map<Long, Map<? extends Class<? extends GenericResponse>, ? extends SomeClient<? extends GenericResponse>>>

instead of

Map<Long, Map<Class<? extends GenericResponse>, SomeClient<? extends GenericResponse>>>

By the way I've replaced ? super with ? extends in my example because I think you don't use them correctly.

Radu Dumbrăveanu
  • 1,104
  • 1
  • 18
  • 23
-1

I figured out the issue. In the below statement

someClientMap.put(clientId,  new HashMap<Class<T>,SomeClient<T>>());

The new HashMap I am creating is more restrictive than what the outer map accepts. since T extends GenericResponse in the generic method.

The declaration allows for different subclasses of the GenericResponse class to be stored in the same map, since I am using super

private static final Map<Long, Map<Class<? super GenericResponse>, SomeClient<? super GenericResponse>>> someClientMap 
   = new HashMap<Long, Map<Class<? super GenericResponse>, SomeClient<? super GenericResponse>>>(); // <? super GenericResponse> should enable me to put subclasses of type GenericResponse in the Map

Please refer Difference between <? super T> and <? extends T> in Java for a very good explaination of the same.

To resolve the issue I replaced the put statement to

someClientMap.put(clientId,  new HashMap<Class<? super GenericResponse>,SomeClient<? super GenericResponse>>());

This put ensures that the inner map can indeed hold instances of different subclasses of the GenericResponse class as was declared in the initial declaration.

 private static final Map<Long, Map<Class<? super GenericResponse>, SomeClient<? super GenericResponse>>> someClientMap 
   = new HashMap<Long, Map<Class<? super GenericResponse>, SomeClient<? super GenericResponse>>>();
rohit.agrawal
  • 9
  • 1
  • 1
  • 4
  • You use `? super GenericResponse` but in the text you say that you need to "hold instances of different **subclasses** of the `GenericResponse` class" - this is confusing, either use `? super GenericResponse` and say "superclasses of the `GenericResponse` class" or use `? extends GenericResponse` and say "subclasses of the `GenericResponse` class" – Radu Dumbrăveanu Aug 09 '17 at 11:37
  • when I use super GenericResponse> and say "hold instances of different subclasses of the GenericResponse class" I mean it. It is a little counter-intuitive but that is how it is. Please refer the accepted answer for https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java for a very good explanation of the same. – rohit.agrawal Aug 09 '17 at 14:47