2016-03-28 38 views
7

考慮以下降低測試用例之間曖昧:方法參照一元靜態方法是功能及雙功能參數類型

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編譯被接受,但調用傳遞方法引用被拒絕,並出現以下錯誤:

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(省略一些子彈):

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.

正如我解釋它,BiFunction的功能類型元數是2,但copyOf所有重載是靜態的,具有元數1,因此該方法引用不是潛在的兼容與BiFunction參數,因此replaceAllSecond(BiFunction)不適用。

我誤解了JLS,還是這是一個javac錯誤? JDK-8026231描述了更新javac以實現該規範,但該錯誤在2013年第一版Java 8之前(2014年3月)得到了解決。

回答

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月初訪問構建(甚至是很老的像9ea57)而Java-8失敗(即使使用最新的更新)。

+1

有趣的是,存在競爭的'copyOf'重載觸發錯誤;如果只有其中一個,它編譯。感謝您進一步減少它;在完成JLS的工作之後,我很「好,對於一個SO問題的研究就足夠了」。 :) –

+1

@JeffreyBosboom在設計新的API時通常會避免創建僅在已接受的函數接口類型中有區別的重載(即使此類型具有不同的arity),因爲這可能會導致方法引用不明確。即使沒有這個bug,假設下一個Guava版本將添加ImmutableList.copyOf(無論foo,無論吧)(他們有這樣做的道德權利):這可能會破壞現有的代碼,它使用你的'PairList.replaceAllSecond (ImmutableList :: copyOf)'。在這種情況下,我使用不同的方法名稱。 –