Incompatible types error while trying to create a map of maps

806 views Asked by At

I am trying to create a map of maps using the ConcurrentSkipListMap. If I create a simple map example it seems to be fine:

Map<Integer, Integer> mmap2 = new ConcurrentSkipListMap<Integer, Integer>();

Once I try to create a map of maps, I get a Incompatible types error:

Map<Integer,  Map<Integer, Integer>> mmap = 
   new ConcurrentSkipListMap<Integer, ConcurrentSkipListMap<Integer, Integer>>();

If I switch the definition to include a ConcurrentSkipListMap, its compiles with no problems:

Map<Integer,  ConcurrentSkipListMap<Integer, Integer>> mmap = 
   new ConcurrentSkipListMap<Integer, ConcurrentSkipListMap<Integer, Integer>>();

Why cant I define the map of map's using the Map interface?

4

There are 4 answers

0
Cruncher On BEST ANSWER

I can answer the question with an example.

Map<Integer, Map<Integer, Integer> mmap = new ConcurrentSkipListMap<Integer, ConcurrentSkipListMap<Integer, Integer>>();

mmap.put(5, new HashMap<Integer, Integer>());

In this case, do you expect the put line to be allowed? If it is not allowed then it breaks the definition of mmap. If it is allowed then it breaks the right hand side.

You've produced a line a code that whether it works or not gives you a contradiction. Therefore we don't allow such definitions of mmap.

2
Prabhaker A On

Inheritance is not applied to Generics type parameters.
You can use wild cards as below.

   Map<Integer,  ? extends Map<Integer, Integer>> mmap = new ConcurrentSkipListMap<Integer, ConcurrentSkipListMap<Integer, Integer>>();  

More info read java subtyping

7
Ravi K Thapliyal On

The concept of Polymorphism doesn't extend to Java generics the same way as they do to classes. That's why, ConcurrentSkipListMap<Integer, ConcurrentSkipListMap<Integer, Integer>> isn't considered as a subtype of Map<Integer, Map<Integer, Integer>> and hence cannot be assigned.

The reason for this is that generics only provides compile-time type safety. At run-time the generic type is not known due to what is known as type erasure. So, basically compiler is trying to prevent this

// if this was allowed
List<Shape> shapes = new ArrayList<Circle>();

// and some place else in your code
shapes.add(new Square()); // Square now fits in a Circle list

This would break the ArrayList's generic type and would throw no errors; because, which type is valid and which is not, isn't known at run-time. But, if you say, "Hey, that's what I want! Square to go in a list of Shapes." Then define the list so using new ArrayList<Shape>() and the compiler would comply.

So, you just need to make your assignment as

Map<Integer,  Map<Integer, Integer>> mmap = 
                  new ConcurrentSkipListMap<Integer, Map<Integer, Integer>>();

i.e. preferring the use of Interfaces consistent on both the sides while using generics.

EDIT : (In response to @PaulBellora's downvote)

There's a reason why you can assign a Circle[] to Shape[] but not ArrayList<Circle> to ArrayList<Shape>. And, the reason is that if your code tries to add a Square to a Circle[] through a Shape[] reference, you would get an ArrayStoreException at runtime because JVM would know the actual type of the array.

But, due to type erasure the same runtime type safety cannot be extended to collections and hence generic types are not co-variant. If the question was why have the type erased then if knowing it at runtime could clearly have benefits; the answer would be to play nice with pre-Java 5 code base.

1
Amaldev On

you can try this here you will have Map reference inside Map object

public class GenericTest {

    void fun(){
        Map<Integer, Map<Integer, Integer>> mmap = new HashMap<Integer, Map<Integer, Integer>>();

        Map<Integer, Integer> map = new HashMap<Integer, Integer>();

        mmap.put(5,map);
    }
}