2

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?

SwDevMan81
  • 48,814
  • 22
  • 151
  • 184
  • This is very interesting actually. I would have expected that to work. – Cruncher Aug 28 '13 at 12:31
  • Ah, yes it does make sense that it shouldn't work. When you create a "new ConcurrentSkipListMap>()" you don't actually instantiate the(second) skiplistmap. As a result your map that you created would not accept maps which are not skiplist maps, which the original definition must guarentee – Cruncher Aug 28 '13 at 12:34
  • possible duplicate of [Is List a subclass of List? Why aren't Java's generics implicitly polymorphic?](http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p) – Paul Bellora Aug 28 '13 at 15:28

4 Answers4

3

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.

Cruncher
  • 7,641
  • 1
  • 31
  • 65
0

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

Prabhaker A
  • 8,317
  • 1
  • 18
  • 24
0

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.

Ravi K Thapliyal
  • 51,095
  • 9
  • 76
  • 89
  • 1
    -1 The reason for generics not being covariant is not type erasure. – Paul Bellora Aug 28 '13 at 15:01
  • @PaulBellora, added my response as an update. Please, do leave a response on why you think you're correct. – Ravi K Thapliyal Aug 28 '13 at 16:05
  • @TheOtherDownVoter, Please care to leave a response. It's always nice to meet people who can back up what they say. – Ravi K Thapliyal Aug 28 '13 at 16:13
  • That generics aren't covariant was a language design decision - no doubt helped along by type erasure, but only coincidentally. Array covariance is largely regarded as a mistake in retrospect. C# for example has reified generics, but generics are still not covariant, because it leads to type-unsafe code (see [this article](http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx)). – Paul Bellora Aug 28 '13 at 16:16
  • Well, obviously @Paul my answer only applies to Java. I never meant it to come across as *generic* (pun intended :). If you consider my answer in context of Java, we're saying the same thing from the two ends of it: caused by *type erasure* introduced because of existing pre-generics code and design decision and thus the *type erasure*. I really hope you see that. – Ravi K Thapliyal Aug 28 '13 at 16:28
  • I mentioned C# as an *example*, the point being that covariant generics are type-unsafe, regardless of implementation. – Paul Bellora Aug 28 '13 at 17:34
  • [Here it is straight from the Skeet's mouth](http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p#comment25359397_2745301) if that helps ;) – Paul Bellora Aug 28 '13 at 17:45
-1

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);
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Amaldev
  • 983
  • 8
  • 10