Reputation: 525
class SuperCl {}
class A extends SuperCl {}
class B extends SuperCl {}
static void method(Map<Integer, List<? extends SuperCl>> map) {}
public static void main(String[] args) {
method(new HashMap<Integer, List<A>>()); //ERROR
}
The compile time error is that the types are incompatible:
Map<Integer, List<A>>
cannot be converted to Map<Integer, List<? extends SuperCl>>
How can I fix it and where does the error come from? I assume it comes from the "method" being static.
EDIT: I changed the map implementation to HashMap (copy error) - this should not change anything
Upvotes: 0
Views: 242
Reputation: 122439
Generics are invariant. For parameterized types to be compatible, their type arguments must match exactly, unless one of them is a wildcard at the top level. Map<Integer, List<A>>
is not a subtype of Map<Integer, List<? extends SuperCl>>
because List<A>
is not identical to List<? extends SuperCl>
. Yes, List<A>
is a subtype of List<? extends SuperCl>
, but they are not identical, which is what is needed.
As you may know, List<Dog>
is not a subtype of List<Animal>
, even though Dog
is a subtype of Animal
. It's the same situation here. A subtype relationship of the type arguments does not lead to a subtype relationship of the parameterized types (that would be called "covariant"; Java array types are covariant, but generics are not).
One solution to this is to use a wildcard at the top level. For example, List<Dog>
is a subtype of List<? extends Animal>
. Similarly in your case, Map<Integer, List<A>>
is a subtype of Map<Integer, ? extends List<? extends SuperCl>>
. So you can declare your method as:
static void method(Map<Integer, ? extends List<? extends SuperCl>> map) {}
Upvotes: 0
Reputation: 321
This works:
class SuperCl {}
class A extends SuperCl {}
class B extends SuperCl {}
static <T extends SuperCl> void method(Map<Integer, List<T>> map) {}
public static void main(String[] args) {
method(new HashMap<Integer, List<A>>()); //NO MORE ERROR
}
I simply moved the generics:
<T extends SuperCl>
to the static method declaration. This makes it verifiable at compile time. On the other hand, having that generic at the method argument is not compile time verifiable.
Upvotes: 0
Reputation: 140318
A HashMap<Integer, List<A>>
isn't a Map<Integer, List<? extends SuperCl>>
, because you can add any type of List<? extends SuperCl>
to the latter.
For example:
Map<Integer, List<A>> original = new HashMap<Integer, List<A>>();
// Raw types to intentionally break the type system.
Map<Integer, List<? extends SuperCl>> map = (Map) original;
List<B> listOfB = new ArrayList<>();
listOfB.add(new B());
map.put(0, listOfB);
List<A> listOfA = original.values().iterator().next();
A item = listOfA.get(0); // ClassCastException.
If you could do that, you'd have been able to add a value that's not a List<A>
to it. Hence it's not allowed.
You could change the type in the method signature to this, for example:
Map<Integer, ? extends List<? extends SuperCl>>
and that would be fine, because you can't put any value into that (other than literal null
).
Upvotes: 3
Reputation: 18133
Change your method to
static <T extends SuperCl> void method(Map<Integer, List<T>> map) {
}
Edit: The error mainly comes from the use of a nested generic. If you would have something like
static void method (List<? extends SuperC1> list) {
}
public static void main (String[] args) {
List<A> list = new ArrayList<>();
method(list);
}
you would not get a compile time error because A
satisfies ? extends SuperCl
.
Upvotes: 3