对于一元静态方法的方法引用在Function和BiFunction参数类型之间是不明确的

Jef*_*oom 7 java jls java-8 method-reference

考虑以下简化的测试用例:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

使用Oracle JDK 8u40中的javac进行编译,replaceAllSecond接受对lambda的调用,但传递方法引用的调用被拒绝,并出现以下错误:

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
Run Code Online (Sandbox Code Playgroud)

我不明白为什么过载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,因此方法引用不可能与BiFunction参数兼容,因此replaceAllSecond(BiFunction)不太可能适用.

我是否误解了JLS,或者这是一个javac错误? JDK-8026231描述了更新javac以实现规范,但该错误在2013年第一版 Java 8(2014年3月)之前得到解决.

Tag*_*eev 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
    }
}
Run Code Online (Sandbox Code Playgroud)

我想这是一个javac问题,因为这个代码可以很好地编译Java-9 Early Access版本(甚至是像9ea57这样的旧版本),而Java-8则失败了(即使是最新的更新).