Blacklabel
Blacklabel

Reputation: 846

Generic function for Map of maps in Java

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?

Upvotes: 2

Views: 621

Answers (4)

Paul Bellora
Paul Bellora

Reputation: 55233

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) { }

Upvotes: 2

Matt
Matt

Reputation: 11815

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.

Upvotes: 2

Jules
Jules

Reputation: 559

static void test(Map<Integer, Map<Integer, ? extends Number>> a) { }

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

Upvotes: 0

Pshemo
Pshemo

Reputation: 124275

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
}

Upvotes: 3

Related Questions