On Fri, 10 Jun 2022 at 14:10, Sumanth Rajkumar <[email protected]>
wrote:
> For 1) I ran mvn verify and found the following errors
>
>
> org.apache.commons.numbers.complex.Complex.getImaginary():METHOD_NOW_ABSTRACT,
> org.apache.commons.numbers.complex.Complex.getReal():METHOD_NOW_ABSTRACT,
> org.apache.commons.numbers.complex.Complex:CLASS_NOW_ABSTRACT,
> org.apache.commons.numbers.complex.Complex:CLASS_TYPE_CHANGED,
>
> org.apache.commons.numbers.complex.ComplexCartesianImpl:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE,
>
> org.apache.commons.numbers.complex.ComplexList:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE,
>
> org.apache.commons.numbers.complex.ImmutableComplexList:METHOD_ABSTRACT_ADDED_IN_IMPLEMENTED_INTERFACE
>
> These are expected changes... Is there a compatibility issue here?
>
Yes. These are reported as errors.
One point here is that Complex is a final class. So some of the errors
regarding inheritance are non-issues. However binary compatibility requires
that a class compiled against the old version can run against the new one.
The main issue is changing a class to an interface (type change).
The japicmp plugin does not have a lot of documentation. You can try revapi
instead. Put this in the plugins section of the pom.xml:
<plugin>
<groupId>org.revapi</groupId>
<artifactId>revapi-maven-plugin</artifactId>
<version>0.14.6</version>
<dependencies>
<dependency>
<groupId>org.revapi</groupId>
<artifactId>revapi-java</artifactId>
<version>0.26.1</version>
</dependency>
</dependencies>
</plugin>
Then run:
mvn revapi:check (if you have the jar package)
mvn package revapi:check -DskipTests -Djavadoc.skip (if you don't)
This will output some similar compatibility errors and link to online
documentation that explains why it is a problem.
This is the main issue:
https://revapi.org/revapi-java/differences.html#java.class.kindChanged
Changing a class to an interface. In this case methods on the object are
invoked using a different instruction.
Save this as Test.java:
public class Test {
static class A {
public int getAnswer() { return 42; }
}
interface B {
int getAnswer();
}
static class Bimp implements B {
public int getAnswer() { return 13; }
}
public static void main(String[] args) {
A a = new A();
System.out.println(a.getAnswer());
B b = new Bimp();
System.out.println(b.getAnswer());
}
}
> javac Test.java
> java Test
42
13
> javap -c Test
...
12: invokevirtual #16 // Method Test$A.getAnswer:()I
...
30: invokeinterface #29, 1 // InterfaceMethod
Test$B.getAnswer:()I
...
Here we see the interface method uses 'invokeinterface' and the class
method uses 'invokevirtual'.
So if you change class A to an interface then Test will have a runtime
linkage error as it cannot find the method (this is all assuming that A, B
and Bimp are in their own class files which I didn't do here for
simplicity). Basically if a class thinks the object with a method is a
class and it is changed on the classpath at runtime to an interface this
will not work.
Thus we have to keep Complex as a class. However it does not mean the
methods cannot be moved to e.g.
public final class ComplexFunctions {
interface ComplexResult<R> {
R accept(double real, double imag);
}
public static <R> R conj(double real, double imaginary,
ComplexResult<R> result) {
return result.accept(real, -imaginary);
}
}
And Complex changed to e.g:
ComplexFunctions conj() {
return ComplexMath.conj(real(), imag(), Complex::ofCartesian);
}