Why doesn't the following code produce a compile-time unchecked warning:
class Parent {
public List method(){
return null;
}
}
class Child extends Parent {
public List<String> method() {
return null;
}
}
while the following actually does:
class Parent {
public List<String> method(){
return null;
}
}
class Child extends Parent {
public List method() {
return null;
}
}
I'm actually looking for a reference for this behaviour in the JLS.
Because List
is not a subtype of List<String>
but (as you know) List<String>
is a subtype of List
.
Examples given in the JLS:
class C implements Cloneable {
C copy() throws CloneNotSupportedException {
return (C)clone();
}
}
class D extends C implements Cloneable {
D copy() throws CloneNotSupportedException {
return (D)clone();
}
}
This represents your first example where the return type in the child is a subtype of the parent. Likewise, in your example, List<String>
is a subtype of List
.
4.10.2. Subtyping among Class and Interface Types
Given a generic type declaration
C<F1,...,Fn>
(n > 0), the direct supertypes of the generic typeC<F1,...,Fn>
are all of the following:
- The direct superclass of
C<F1,...,Fn>
.- The direct superinterfaces of
C<F1,...,Fn>
.- The type
Object
, ifC<F1,...,Fn>
is a generic interface type with no direct superinterfaces.- The raw type
C
. (boldness mine)
While:
class StringSorter {
// turns a collection of strings into a sorted list
List<String> toList(Collection<String> c) {...}
}
class Overrider extends StringSorter {
List toList(Collection c) {...}
}
is an example of your second snippet.
An unchecked warning would be given when compiling
Overrider
against thenewdefinition ofStringSorter
because the return type ofOverrider.toList
isList
, which is not a subtype of the return type of the overridden method,List<String>
. (striking mine)
JLS examples 8.4.8.3-1 & 8.4.8.3-2 and specifically:
8.4.8.3. Requirements in Overriding and Hiding
If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable (§8.4.5) for d2, or a compile-time error occurs.
This rule allows for covariant return types - refining the return type of a method when overriding it.
If R1 is not a subtype of R2, a compile-time unchecked warning occurs unless suppressed by the
SuppressWarnings
annotation (§9.6.4.5).