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?
Abstract is used to define something that requires additional definition of functionality before it is considered "complete" (or concrete in Java-certification-test-terms). Generic means it's a class that can handle a wide variety of data types that you define when you instantiate the class.
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
Generic abstraction consists of 'selectively suppressing information instantiated in a representation, so as to make inferences that pertain only to the generic case' (Nersessian, 2008, p. 193).
Abstract Class Now generics seem cool but they really shine when used with abstract classes. An abstract class is a class that itself is never intended to be instantiated, instead they are used to pass properties to sub classes via inheritance.
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With