2016-03-28 6 views
7

mir der folgenden reduziert Testfall:Method Bezug auf unären statisches Verfahren ist zweideutig zwischen Funktion und bifunktionellen Parametertypen

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

Kompilieren mit Javac von Oracle JDK 8u40, der Aufruf von replaceAllSecond mit einem Lambda akzeptiert wird, aber die rufen Sie eine Methode Referenz mit folgendem Fehler abgelehnt begegnen:

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 

ich verstehe nicht, warum die Überlastung unter BiFunction hier potentiell anwendbar ist. Von JLS 15.12.2.1 (mit einigen Kugeln weggelassen):

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.

Wie ich es interpretieren BiFunction ‚, Funktionstyp arity s 2, aber alle Überlastungen von copyOf sind statisch und haben arity 1, so dass die Methode Referenz nicht potentiell kompatibel ist mit dem BiFunction Parameter und so replaceAllSecond(BiFunction) ist nicht potenziell anwendbar.

Fehle ich die JLS falsch, oder ist das ein Javac Bug? JDK-8026231 beschreibt die Aktualisierung von JavaC zum Implementieren der Spezifikation, aber dieser Fehler wurde 2013 vor der ersten Version von Java 8 (März 2014) behoben.

Antwort

5

Ihr Beispiel auf die folgenden reduziert werden könnte:

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

Ich nehme an, das ein Javac Problem ist, da dieser Code mit Java-9 Early Access baut (auch ganz alte wie 9ea57) fein kompiliert, während es mit Java-8 fehlschlägt (selbst mit den neuesten Updates).

+1

Interessant ist, dass das Vorhandensein konkurrierender 'copyOf'-Überladungen den Fehler auslöst; Wenn es nur eines von beiden gibt, kompiliert es. Danke für die weitere Reduzierung; Ich war irgendwie in "in Ordnung, das ist genug Forschung für eine SO Frage" -Modus nach der Arbeit durch die JLS. :) –

+1

@ JeffreyBosboom im Allgemeinen, wenn ich neue APIs entwerfe, vermeide ich das Erzeugen von Überladungen, die sich nur im akzeptierten funktionalen Schnittstellentyp unterscheiden (auch wenn dieser Typ unterschiedliche Arität hat), weil dies eine Mehrdeutigkeit mit Methodenreferenzen verursachen kann. Auch wenn dieser Fehler nicht vorhanden ist, wird angenommen, dass die nächste Guava-Version 'ImmutableList.copyOf (Was auch immer foo, was auch immer bar)' hinzufügen wird (sie haben das moralische Recht, dies zu tun): das könnte bestehenden Code brechen, der Ihre 'PairList.replaceAllSecond verwendet (ImmutableList :: copyOf) '. In solchen Fällen verwende ich verschiedene Methodennamen. –

Verwandte Themen