考虑以下简化的测试用例:
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
}
}
使用Oracle JDK 8u40中的javac进行编译,接受带有lambda的replaceAllSecond
调用,但传递方法引用的调用被拒绝,并出现以下错误:
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
我不明白为什么过载BiFunction
可能适用于此处。来自JLS 15.12.2.1(省略了一些项目符号):
当且仅当满足以下所有条件时,成员方法才可能适用于方法调用:
- 如果成员是arity n的固定arity方法,则方法调用的arity等于n,对于所有i(1≤i≤n),方法调用的第i个参数可能兼容,如下所述,使用方法的第i个参数的类型。
根据以下规则,表达式可能与目标类型兼容:
方法引用表达式(第15.13节)可能与函数接口类型兼容,如果类型的函数类型arity为n,则至少存在一个可能适用于arity n的方法引用表达式的方法( §15.13.1),以下之一是真的:
- 方法引用表达式具有ReferenceType :: [TypeArguments]标识符的形式,并且至少一个可能适用的方法是i)static并支持arity n,或ii)not static并支持arity n-1。
正如我解释的那样,BiFunction
的函数类型arity是2,但copyOf
的所有重载都是静态的并且具有arity 1,因此方法引用可能与{{1}不兼容参数,因此BiFunction
可能不适用。
我是否误解了JLS,或者这是一个javac错误? JDK-8026231描述了更新javac以实现规范,但该错误在2013年第一版Java 8(2014年3月)之前得到解决。
答案 0 :(得分:5)
您的示例可以进一步简化为以下内容:
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
}
}
我认为这是一个javac问题,因为这段代码可以很好地编译Java-9 Early Access版本(甚至是像9ea57那样很老的版本),而Java-8则失败了(即使是最新的版本)。