java方法对一元静态方法的引用在函数和双函数参数类型之间是不明确的

nx7onnlm  于 2021-07-13  发布在  Java
关注(0)|答案(1)|浏览(387)

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

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
    }
}

用javac从oraclejdk8u40编译,调用 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

我不明白为什么超载 BiFunction 可能适用于这里。根据jls 15.12.2.1(省略部分项目符号):
当且仅当以下所有条件均为真时,成员方法才可能适用于方法调用:
如果成员是arity为n的固定arity方法,则方法调用的arity等于n,对于所有i(1≤ 我≤ n) ,方法调用的第i个参数可能与方法的第i个参数的类型兼容,如下所述。
根据以下规则,表达式可能与目标类型兼容:
方法引用表达式(§15.13)与函数接口类型潜在兼容,如果该类型的函数类型arity为n,则至少存在一个arity为n的方法引用表达式的潜在适用方法(§15.13.1),且下列情况之一成立:
方法引用表达式的格式为referencetype::[typearguments]identifier,并且至少有一个可能适用的方法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年得到解决,在java8的第一个版本发布之前(2014年3月)。

yfwxisqw

yfwxisqw1#

您的示例可以进一步简化为以下内容:

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早期的access构建(甚至像9ea57这样非常老的构建)中编译得很好,而在java-8中编译失败(即使是最新的更新)。

相关问题