Let's have this interface
interface Foo {
List<String> foo(String arg1, String arg2);
}
And a class implementing it
class Bar implements Foo {
@Override
public List<String> foo(String arg1, String arg2) {
//implementation...
}
}
We have:
Bar#foo
asd1
List<String>
return type in d1 as R1.Foo#foo
asd2
List<String>
return type in d2 as R2.
R1, adapted to the type parameters of d2 (§8.4.4), is a subtype of R2.
It means that R1
can be a subtype of R2
, which means R1 should pass the IS-A test. So, we can do the following:
class Bar implements Foo {
@Override
public ArrayList<String> foo(String arg1, String arg2) {
//implementation...
}
}
R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9).
This is more related to generics. It means that R1
should pass the IS-A test even if it throws a warning for unchecked overriding. So, we can do the following:
class Bar implements Foo {
@Override
public ArrayList foo(String arg1, String arg2) {
//implementation...
}
}
d1 does not have the same signature as d2 (§8.4.2), and R1 = |R2|
This means overloading:
class Bar implements Foo {
@Override
public ArrayList<String> foo(String arg1, String arg2) {
//implementation...
}
public ArrayList<String> foo(String arg1, String arg2, String arg3) {
//implementation...
}
}