Why do subcontracting and untested rules work this way by return type when overriding a generic method with a non-generic one?

public class Base { <T> List<? extends Number> f1() {return null;} List<? extends Number> f2() {return null;} <T extends Number> List<T> f3() {return null; } } class Derived extends Base { List<String> f1() {return null;} // compiles fine !!! List<String> f3() {return null; } // compiles fine !!! // compile ERR: return type is incompatible with Base.f2() List<String> f2() {return null;} } 

Why does the definition of the overriding methods f1 () and f3 () in the Derived class not give a compilation error, how is the definition of the redefining methods f2 () in the return type of the Derived class (which gives a compilation error) incompatible with Base.f2 () ")?

The character override rule in JLS allows you to abandon the override method (in the Derived class), and the overridden method (in the base class) is common.

An invalid override rule allows the return type in a subclass of List<String> instead of List<T> in the base class.

But I canโ€™t explain the difference in behavior below, and I donโ€™t understand why f1 () and f3 () override the definitions in the Derived class compile successfully (on Eclipse, SE8), ignoring the restrictions imposed by the limited type parameter for f3 () and the limited wildcard for f1 () !

PS My assumption is that in f1 () and f3 () in the derived compiler it processes both methods, since it returns only a raw list, the compiler first erases (currently only in Derived !?), and then compares these erasable methods in Derived with unerased (so far) in the database. Now the uncontrolled redefinition rule is in order (and there is no need to check the borders - it is simply impossible), the compiler decides that this correct redefinition and compilation goes further ... and somewhere at the end of the compilation generics in Base.f1 () and Base.f3 () is also deleted :)))

This SO answer also adds ideas to this topic.

+5
source share
1 answer

Your redefinitions of f1 and f3 are not common, even though the original declarations are common. Compilations allow the return type of overrides to vary from the original return types, since they have an erase of the same type ( List ). I think this follows from JLS 8.4.5 , although, frankly, I find this part of the specification a bit confusing.

If you change the overrides again:

 <T> List<String> f1() {return null;} <T extends Number> List<String> f3() {return null; } 

... then both will not compile:

 error: <T#1>f1() in Derived cannot override <T#2>f1() in Base <T> List<String> f1() {return null;} ^ return type List<String> is not compatible with List<? extends Number> where T#1,T#2 are type-variables: T#1 extends Object declared in method <T#1>f1() T#2 extends Object declared in method <T#2>f1() error: <T#1>f3() in Derived cannot override <T#2>f3() in Base <T extends Number> List<String> f3() {return null; } ^ return type List<String> is not compatible with List<T#1> where T#1,T#2 are type-variables: T#1 extends Number declared in method <T#1>f3() T#2 extends Number declared in method <T#2>f3() 

Please note that even in javac source code a warning about unsafe conversions is issued, and if you use -Xlint:unchecked , it gives details:

 return type requires unchecked conversion from List<String> to List<? extends Number> return type requires unchecked conversion from List<String> to List<T> 
+3
source

Source: https://habr.com/ru/post/1272971/


All Articles