函数和BiFunction参数类型

时间:2016-03-28 06:47:56

标签: java java-8 jls 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
    }
}

使用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月)之前得到解决。

1 个答案:

答案 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则失败了(即使是最新的版本)。