I tested this.
Unfortunately there is a NoSuchMethodError exception at the point of the call when a method's return type is changed to a subtype of the original type.
Making this change is thus not binary backwards compatible.
This is too bad...
Test setup
test
├── c
├── c_int
│ └── C.java
├── c_num
│ └── C.java
└── Test.java
test/c_num/C.java:
package test.c;
public class C {
public Number f() {
System.out.println("f Number");
return 0.0;
}
}
test/c_int/C.java:
package test.c;
public class C {
public Integer f() {
System.out.println("f Integer");
return 1;
}
}
test/Test.java:
package test;
import test.c.C;
public class Test {
public static void main(String[] args) throws Exception {
C b = new C();
Number n = b.f();
System.out.println(n);
}
}
Test
Compile the two C classes with different return type:
$ javac test/c_int/C.java
$ javac test/c_num/C.java
Compile Test against C with return type Number:
$ cp test/c_num/C.class test/c/
$ javac test/Test.java
Run Test against C with return type Number:
$ java test.Test
f Number
0.0
Run Test against C with return type Integer, without recompiling Test:
$ cp test/c_int/C.class test/c/
$ java test.Test
Exception in thread "main" java.lang.NoSuchMethodError: 'java.lang.Number test.c.C.f()'
at test.Test.main(Test.java:8)
Bytecode
We can also see in the byte code that the method call to f contains the return type of the method:
$ javap -c test/Test.class
Compiled from "Test.java"
public class test.Test {
public static void main(java.lang.String[]);
Code:
...
9: invokevirtual #4 // Method test/c/C.f:()Ljava/lang/Number;
...
}
Eclipse document
The Eclipse foundation maintain a document called Evolving Java-based APIs.
The document states this about binary compatibility:
Evolving API classes - API methods and constructors
....
Change result type (including void) - Breaks compatibility
This does not make an exception about changes that only narrow the return type. All changes to the return type breaks binary compatibility.