Reputation: 8396
Why does the following happen:
public class one{
public <T extends Foo> Bar<Foo> function1() {}
public Bar<Foo> function2(){}
}
public class two<F extends Foo> extends one{
public Bar<F> function1(){} //Doesn't throw an error
public Bar<F> function2(){} //Throws an error
}
By saying <T extends Foo>
am I saying that Foo can be overridden with a super type?
Note: My question is not why function2()
throws an error...but why function1()
doesn't throw an error.
Upvotes: 9
Views: 313
Reputation: 19682
This is probably a compiler bug.
The reason that Two.function1
is considered an overriding method of One.function1
is from http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.2
the signature of
Two.function1
is the same as the erasure of the signature ofOne.function1
.
This is to allow legacy subtype (Two) still compile after a supertype (One) is generified.
After that, javac needs to check the return type:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8.3
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.
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.5
R1 is either a subtype of R2 or R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9), or R1 = |R2|
http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.9
There is an unchecked conversion from the raw class or interface type (§4.8) G to any parameterized type of the form G.
Unchecked conversion does not apply here where R1=Bar<F>, R2=Bar<Foo>
. That's why Javac reports that function2
return type is conflicting.
Javac should report the same error for function1
. I'm guessing that in a previous step, javac took the erasure of One.function1
, which is Bar function1()
, and mistakenly uses that version to check against Two.function1
- here R2=Bar
, therefore R1
is a subtype($4.10.2) of R2
, therefore the return type is compatible. (However, if that theory is correct, there shouldn't be a "unchecked" warning)
Upvotes: 6
Reputation: 3254
<T extends Foo>
defines a type T
that must be some subtype of Foo
. This isn't actually used anywhere in one
or two
.
My guess is this is because how the compiler is erasing the types for the functions is being handled differently since there is a generic type definition scoped to one.function1()
.
Looking at the JLS 8.4.8.3 defines valid method override. There is an example a bit down about the "unchecked conversion" warning. In there, it links to 8.4.5 describing what is "return-type-substitutable" and from there to 5.1.9 about unchecked conversions.
I do not have an answer, but it appears that something about tagging that method as a generic method (not just a method using a parameterized type) triggers allowing the unchecked conversion to be done - whether intentionally or because of a bug.
Edit: Given
public class Main {
class Bar<X> {}
class Foo{}
class one{
public <T extends Foo> Bar<Foo> function1() { return null; }
public Bar<Foo> function2(){ return null; }
}
class two<F extends Foo> extends one{
public Bar<F> function1(){ return null; } //Doesn't throw an error
public Bar<F> function2(){ return null; } //Throws an error
}
}
Compiling with javac -Xlint:unchecked Main.java
:
Main.java:9: warning: [unchecked] function1() in Main.two overrides <T>function1() in Main.one
public Bar<F> function1(){ return null; } //Doesn't throw an error
^
return type requires unchecked conversion from Main.Bar<F> to Main.Bar<Main.Foo>
where F,T are type-variables:
F extends Main.Foo declared in class Main.two
T extends Main.Foo declared in method <T>function1()
Main.java:10: error: function2() in Main.two cannot override function2() in Main.one
public Bar<F> function2(){ return null; } //Throws an error
^
return type Main.Bar<F> is not compatible with Main.Bar<Main.Foo>
where F is a type-variable:
F extends Main.Foo declared in class Main.two
1 error
1 warning
Upvotes: 2
Reputation: 12755
The method function2
is not generic. It returns a Bar<Foo>
.
Subclasses may return subclasses of return types in overridden methods. This is called a covariant return type.
But Bar<F>
is not a subclass of Bar<Foo>
, even if F
subclasses Foo
- generics are not covariant in Java (arrays are, though).
My IDE actually warns me about function1
too: Unchecked overriding: return type requires unchecked conversion. I believe this is only a warning because of the sole existence of a generic type in base class and type erasure. I'd say you're operating on non-generic Bar
s anyway here..
Upvotes: 3