Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method reference to unary static method is ambiguous between Function and BiFunction parameter types

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).

like image 577
Jeffrey Bosboom Avatar asked Mar 28 '16 06:03

Jeffrey Bosboom


1 Answers

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).

like image 173
Tagir Valeev Avatar answered Oct 19 '22 00:10

Tagir Valeev