Consider the following reduced testcase:
import java.util.AbstractList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
public final class Example {
static class PairList<A, B> {
public void replaceAllSecond(Function<? super B, ? extends B> secondFunction) {}
public void replaceAllSecond(BiFunction<? super A, ? super B, ? extends B> secondFunction) {}
}
static class ImmutableList<E> extends AbstractList<E> {
public static <E> ImmutableList<E> copyOf(Iterable<? extends E> elements) {return null;}
public static <E> ImmutableList<E> copyOf(Collection<? extends E> elements) {return null;}
public static <E> ImmutableList<E> copyOf(Iterator<? extends E> elements) {return null;}
public static <E> ImmutableList<E> copyOf(E[] elements) {return null;}
@Override public E get(int index) {return null;}
@Override public int size() {return 0;}
}
public static void foo() {
PairList<Integer, List<Integer>> list = new PairList<>();
list.replaceAllSecond(x -> ImmutableList.copyOf(x)); //accepted
list.replaceAllSecond(ImmutableList::copyOf); //error
}
}
Compiling with javac from Oracle JDK 8u40, the call to replaceAllSecond
with a lambda is accepted, but the call passing a method reference is rejected with the following error:
Example.java:26: error: reference to replaceAllSecond is ambiguous
list.replaceAllSecond(ImmutableList::copyOf); //error
^
both method replaceAllSecond(Function<? super B,? extends B>) in PairList and method replaceAllSecond(BiFunction<? super A,? super B,? extends B>) in PairList match
where B,A are type-variables:
B extends Object declared in class PairList
A extends Object declared in class PairList
1 error
I don't understand why the overload taking BiFunction
is potentially applicable here. From JLS 15.12.2.1 (with some bullets omitted):
A member method is potentially applicable to a method invocation if and only if all of the following are true:
- If the member is a fixed arity method with arity n, the arity of the method invocation is equal to n, and for all i (1 ≤ i ≤ n), the i'th argument of the method invocation is potentially compatible, as defined below, with the type of the i'th parameter of the method.
An expression is potentially compatible with a target type according to the following rules:
A method reference expression (§15.13) is potentially compatible with a functional interface type if, where the type's function type arity is n, there exists at least one potentially applicable method for the method reference expression with arity n (§15.13.1), and one of the following is true:
- The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable method is i) static and supports arity n, or ii) not static and supports arity n-1.
As I interpret it, BiFunction
's function type arity is 2, but all overloads of copyOf
are static and have arity 1, so the method reference is not potentially compatible with the BiFunction
parameter and so replaceAllSecond(BiFunction)
is not potentially applicable.
Am I misinterpreting the JLS, or is this a javac bug? JDK-8026231 describes updating javac to implement the specification, but that bug was resolved in 2013, before the first release of Java 8 (in March 2014).
Your example could be further reduced to the following:
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
public final class Example {
static class PairList<A, B> {
public void replaceAllSecond(Function<? super B, ? extends B> secondFunction) {}
public void replaceAllSecond(BiFunction<? super A, ? super B, ? extends B> secondFunction) {}
}
public static <E> List<E> copyOf(Iterable<? extends E> elements) {return null;}
public static <E> List<E> copyOf(Collection<? extends E> elements) {return null;}
public static void foo() {
PairList<Integer, List<Integer>> list = new PairList<>();
list.replaceAllSecond(x -> Example.copyOf(x)); //accepted
list.replaceAllSecond(Example::copyOf); //error
}
}
I suppose this is a javac problem, because this code compiles fine with Java-9 Early Access builds (even quite old ones like 9ea57), while fails with Java-8 (even with the newest updates).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With