Closed garydgregory closed 2 weeks ago
When I execute japicmp on the branch you have mentioned, then I get this output:
[ERROR]...,org.apache.commons.collections4.IteratorUtils.forEach(java.util.Iterator,org.apache.commons.collections4.Closure):METHOD_REMOVED,...
The class with the METHOD_REMOVED
incompatibility is org.apache.commons.collections4.IteratorUtils
(and not IterableUtils
like in your example, right?). Its method forEach
changed from
public static <E> void forEach(final Iterator<E> iterator, final Closure<? super E> closure) {
to
public static <E> void forEach(final Iterator<E> iterator, final Consumer<? super E> closure)
Closure
is a subinterface of Consumer
:
public interface Closure<T> extends Consumer<T> {
So the issue here is that the second method parameter changed from a subclass to the super class, as the subclass Closure
is marked deprecated, right?
And this type of change should not be marked as incompatible as the old type is assignable to the new type?
@siom79
The mistake is mine and I have to ask you to forgive me for wasting your time 🤦
My example mistakenly tested IterableUtils
instead of IteratorUtils
!
To confirm my error, I edited the example:
package test;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.collections4.Closure;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.IteratorUtils;
// java -cp target/classes;%HOMEPATH%\.m2\repository\org\apache\commons\commons-collections4\4.4\commons-collections4-4.4.jar test.ColCompatTest
// java -cp target/classes;%HOMEPATH%\.m2\repository\org\apache\commons\commons-collections4\4.5.0-M2\commons-collections4-4.5.0-M2.jar test.ColCompatTest
// java -cp target/classes;%HOMEPATH%\.m2\repository\org\apache\commons\commons-collections4\4.5.0-M3-SNAPSHOT\commons-collections4-4.5.0-M3-SNAPSHOT.jar test.ColCompatTest
public class ColCompatTest {
public static void main(String[] args) {
final List<String> list = Arrays.asList("a", "b");
final Closure<? super String> closure = e -> System.out.println(e);
IteratorUtils.forEach(list.iterator(), closure);
IterableUtils.forEach(list, closure);
}
}
and when I run:
java -cp target/classes;%HOMEPATH%\.m2\repository\org\apache\commons\commons-collections4\4.4\commons-collections4-4.4.jar test.ColCompatTest
java -cp target/classes;%HOMEPATH%\.m2\repository\org\apache\commons\commons-collections4\4.5.0-M2\commons-collections4-4.5.0-M2.jar test.ColCompatTest
all is well, but this fails:
java -cp target/classes;%HOMEPATH%\.m2\repository\org\apache\commons\commons-collections4\4.5.0-M3-SNAPSHOT\commons-collections4-4.5.0-M3-SNAPSHOT.jar test.ColCompatTest
with:
Exception in thread "main" java.lang.NoSuchMethodError: 'void org.apache.commons.collections4.IteratorUtils.forEach(java.util.Iterator, org.apache.commons.collections4.Closure)'
at test.ColCompatTest.main(ColCompatTest.java:18)
So JApiCmp was correct all along 😄
Yes, if you compile your code against M2 you can see with javap
that it intends to invoke the method with the Closure
parameter:
29: invokestatic #28 // Method org/apache/commons/collections4/IteratorUtils.forEach:(Ljava/util/Iterator;Lorg/apache/commons/collections4/Closure;)V
Once you invoke it against version M3-SNAPSHOT
you get:
Exception in thread "main" java.lang.NoSuchMethodError: 'void org.apache.commons.collections4.IteratorUtils.forEach(java.util.Iterator, org.apache.commons.collections4.Closure)'
at test.Main.main(Main.java:11)
In Apache Commons Collections, we are migrating from old custom functional interfaces to Java's own (
Predicate
,Consumer
, and so on).JApiCmp 0.21.2 incorrectly flags method changes from our custom class
Closure
that extendsConsumer
toConsumer
as METHOD_REMOVED.For example:
Incorrectly says:
Specifically:
See that this is a false positive, you can create a Java class like:
And compile it against:
An example invocation against the "old" library would be:
and against the "new" one (the
mvn
invocation above installed the SNAPSHOT):Both work, hence the FP.
It seems that while JApiCmp detects the add/remove case described in JLS 13.4.14, it does not account for the "matching signature" case described in JLS 13.4.12