2

I am trying to write a generic function which will accept both of the following data types

Map <Integer, Map<Integer, Long>>
Map <Integer, Map<Integer, Double>>

My function looks like this,

function(Map<Integer, Map<Integer, ? extends Number>> arg) {}

But I am getting an incompatible type error. It works for a Map, but not for map of Maps. I am not able to understand why? Is there any way to do this?

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Blacklabel
  • 826
  • 1
  • 10
  • 19

4 Answers4

3

You could try something like

static <T extends Number> void function(Map<Integer, Map<Integer, T>> map) {}

public static void main(String[] args) {
    Map<Integer, Map<Integer, Long>> map1 = new HashMap<Integer, Map<Integer, Long>>();
    Map<Integer, Map<Integer, Double>> map2 = new HashMap<Integer, Map<Integer, Double>>();
    Map<Integer, Map<Integer, String>> map3 = new HashMap<Integer, Map<Integer, String>>();
    function(map1);
    function(map2);
    function(map3);// <-- compilation error here, String is not Number
}
Pshemo
  • 122,468
  • 25
  • 185
  • 269
2

Why not just parameterize the method?

public <T extends Number> void function(Map<Integer, Map<Integer, T>>) { ... }

I've found that the wildcard capture tends to confuse people as to what it really does.

Map<Integer, ? extends Number> really means any Map whose key is Integer and whose value is a type derived from Number. This means Map<Integer, Integer>, Map<Integer,Long>.

For this reason, you can never really add to those collections, because of the wildcard the compiler can't tell what the real type is in order to add.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Matt
  • 11,523
  • 2
  • 23
  • 33
2

First let's reduce the problem by using Sets instead:

Set<Set<Long>> longSetSet = null;
Set<Set<Double>> doubleSetSet = null;

Set<Set<? extends Number>> someNumberSetSet;

// try assigning them
someNumberSetSet = longSetSet;   //
someNumberSetSet = doubleSetSet; // compiler errors - incompatible types

At first glance you might wonder why this assignment is illegal, since after all you can assign a Set<Long> to Set<? extends Number> The reason is that generics are not covariant. The compiler prevents you from assigning a Set<Set<Long>> to Set<Set<? extends Number>> for the same reason it won't let you assign a Set<Long> to a Set<Number>. See the linked answer for more details.

As a workaround, you can use a type parameter in your method signature as other answers have suggested. You can also use another wildcard to make the assignment legal:

Set<? extends Set<? extends Number>> someNumberSetSet;

someNumberSetSet = longSetSet;   //
someNumberSetSet = doubleSetSet; // legal now

Or in your example:

function(Map<Integer, ? extends Map<Integer, ? extends Number>> arg) { }
Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
0
static void test(Map<Integer, Map<Integer, ? extends Number>> a) { }

This actually works just fine for me (JavaSE-1.6).

Jules
  • 487
  • 3
  • 10