2014-10-05 161 views
2

我想了解爲什麼以下情況不能與「引用不明確」的情況一起使用,而應該不是。值得向javac團隊報告嗎?爲什麼這個與lambda有關的類型推斷失敗?

import java.util.function.Function; 
import java.util.function.ToLongFunction; 
import org.junit.Test; 

public class LambdaTest { 

    @FunctionalInterface 
    public static interface CheckedFunction<U,R> 
    { 
     R apply(U value) throws Exception; 
    } 

    @FunctionalInterface 
    public static interface CheckedToLongFunction<T> 
    { 
     long apply(T i) throws Exception; 
    } 

    public static <T,R> Function<T,R> unchecked(CheckedFunction<T,R> func) 
    { 
     return value -> { 
      try 
      { 
       return func.apply (value); 
      } 
      catch(RuntimeException e) 
      { 
       throw e; 
      } 
      catch(Exception e) 
      { 
       throw new RuntimeException(e); 
      } 
     }; 
    } 

    public static <T> ToLongFunction<T> unchecked(CheckedToLongFunction<T> func) 
    { 
     return value -> { 
      try 
      { 
       return func.apply (value); 
      } 
      catch(RuntimeException e) 
      { 
       throw e; 
      } 
      catch(Exception e) 
      { 
       throw new RuntimeException(e); 
      } 
     }; 
    } 

     public void 
    bar(Function<Object,Object> fn) { 

      System.out.println("Function"); 
     } 

     @Test public void 
    test() { 

      bar (a -> a); //OK 
      bar (unchecked (a -> a)); //Should be OK, but receive "reference to unchecked is ambiguous" 
      bar (unchecked ((Object a) -> a)); //OK 
     } 

} 

正如你所看到的,例子很有趣,因爲這兩個CheckedFunctionCheckedToLongFunction採取引用類型,所以明確指定蘭巴的輸入類型似乎添加沒什麼用處。但是,它可以讓代碼編譯。

應該調用unckeched(CheckedFunction)而沒有歧義,因爲unchecked(CheckedToLongFunction)會生成返回原語的lambda,而bar需要Object

我特別尋找JLS的解決方案。如果JLS沒有指定這個,我可以嘗試將它作爲javac錯誤提交。

回答

1

函數聲明unchecked(CheckedToLongFunction<T> func)意在返回ToLongFunctionToLongFunction返回long。在你的論點中,你聲稱這個函數應該被忽略,因爲它返回一個原語(也許拋出了一個運行時異常)。

雖然引入了自動拳擊,但從ToLongFunction的原始結果可以裝箱到Long,使其成爲一個對象。

因此,因爲它已經是相同的情況下,ToLongFunction可以返回兩者Longlong

因此歧義函數。

參見JLS 15.12.2

1 - 第一階段(§15.12.2.2)執行重載而不允許拳擊或取消裝箱轉換,或使用可變元數的方法調用的。如果在此階段沒有找到適用的方法,則處理繼續到第二階段。

然後

2-第二階段(§15.12.2.3)執行重載解析,同時允許裝箱和拆箱,但仍排除使用可變元數的方法調用的。如果在此階段沒有找到適用的方法,則處理繼續到第三階段。

+2

「ToLongFunction」類型不應該在這裏。這是'unchecked'的含義模糊的調用。無論如何,'bar'需要一個'Function',而不是'ToLongFunction'。 – 2014-10-05 20:05:29

+0

我想指出,如果指定了lambda的* input *類型,則解析工作正常。指定輸入類型不會解決任何由自動裝箱造成的歧義,因此它不是一個因素。 – 2014-10-08 20:20:38

2

你有兩種方法

public static <T,R> Function<T,R> unchecked(CheckedFunction<T,R> func) 
public static <T> ToLongFunction<T> unchecked(CheckedToLongFunction<T> func) 

每個人都有一個功能接口類型的參數。

@FunctionalInterface 
public static interface CheckedFunction<U, R> { 
    R apply(U value) throws Exception; 
} 

@FunctionalInterface 
public static interface CheckedToLongFunction<T> { 
    long apply(T i) throws Exception; 
} 

implicitly typed lambda expressiona -> a可以給予適當的上下文中應用其中的任一的。但是,在執行重載解析時,該上下文似乎沒有被檢查。

因此,儘管a -> a

bar(unchecked(a -> a)); // Should be OK, but receive 

不能轉換到一CheckedToLongFunction實例(由於Long類型參數不能從該調用上下文中推斷),拉姆達仍然適用於功能型。

編譯器根本無法確定它是否應將a -> a轉換爲CheckedToLongFunctionCheckedFunction。由於兩者都適用,調用是不明確的。調用上下文(和返回類型)不會被檢查以解決歧義。

在這種情況下,然而,

bar(unchecked((Object a) -> a)); // OK 

我們有一個顯式類型 lambda表達式。拉姆達體解析爲類型爲Object的返回值,這不符合CheckedToLongFunction#apply(..)方法的要求,即。 Object類型的表達式不能隱式轉換爲long類型的表達式。

因此,由於還有一個其他適用的方法,選擇一個。

+0

實際上,bar(unchecked(a - > new Object()))'也不起作用,所以它不是指定返回類型的問題。你說調用上下文沒有被選中。爲什麼不?它幾乎看起來像一個編譯器錯誤。 – 2014-10-08 20:27:23

+1

@AleksandrDubinsky lambda表達式'a - > new Object()'仍然是_implicitly typed_。編譯器只會走得這麼遠。因爲它是隱含類型的,所以它不會檢查表達式主體的類型。爲什麼? [閱讀此]。(http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html#jls-18.5.1)似乎,因爲調用可以是獨立的,即。沒有用作任何其他參數或賦值,我們不希望有兩種不同的方式(規則集)來評估它。 – 2014-10-08 21:08:12

相關問題