Reputation: 1559
I've come across something I find odd in Java and haven't been able to find much information on it. Consider the following code:
public class TestClass {
private static abstract class AbstractClass {
abstract List<? extends Object> getList();
abstract Map<Long, List<? extends Object>> getMap();
}
private static final class ConcreteClass extends AbstractClass {
@Override
List<String> getList() {
return null;
}
@Override
Map<Long, List<String>> getMap() {
return null;
}
}
}
The compiler shows an error on the getMap()
method:
getMap() in ConcreteClass cannot override getMap() in AbstractClass
return type Map<Long, List<String>> is not compatible with Map<Long, List<? extends Object>>
But the same error is not present for the getList()
method, yet I would expect either both to work or both to fail. In both cases, the overriding method is delcaring List<String>
in place of List<? extends Object>
. Can someone explain this?
Upvotes: 8
Views: 215
Reputation: 60957
It's because there exists an implicit conversion from List<String>
to List<? extends Object>
, but not from Map<Long, List<String>>
to Map<Long, List<? extends Object>>
.
All generic types are invariant unless you're using wildcard types. Since there is no wildcard in the "outer" Map type's generic type arguments, it can't capture any generic type that doesn't match exactly.
If your map type were Map<Long, ? extends List<? extends Object>>
then it would work like you expect.
And the other part if the answer is that subclasses can override or implement a supertype method with a different return type, but only if the return type of the subtype's method is implicitly convertible to the supertype method's return type. (In Java 1.4 and below even that wouldn't work: it would be a compile time error if the types didn't match exactly.)
Upvotes: 10