2011-04-05 50 views
4

我最近在重構代碼時遇到了這個問題:參數化方法如何在某些情況下隱式約束而不是其他情況?

下面的方法「getList()」有一個參數化的返回類型。在此之下,我已經提出了三種嘗試隱含地將<T>綁定到<Integer>的方法。

我不明白爲什麼前兩個編譯和運行正確,而第三個(bindViaMethodInvocation)甚至不會編譯。

任何線索?

在尋找StackOverflow的類似問題時,我遇到了這個問題: Inferred wildcard generics in return type。答案在那裏(信用Laurence Gonsalves)有幾個有用的參考鏈接來解釋應該怎麼做: 「這裏的問題(如你所建議的)是編譯器執行Capture Conversion。我相信這是一個結果JLS的§15.12.2.6 of the JLS。「或者賦值List<Integer>或從返回List<Integer>方法返回語句 -

package stackoverflow; 

import java.util.*; 

public class ParameterizedReturn 
{ 
    // Parameterized method 
    public static <T extends Object> List<T> getList() 
    { 
     return new ArrayList<T>(); 
    } 

    public static List<Integer> bindViaReturnStatement() 
    { 
     return getList(); 
    } 

    public static List<Integer> bindViaVariableAssignment() 
    { 
     List<Integer> intList = getList(); 
     return intList; 
    } 

    public static List<Integer> bindViaMethodInvocation() 
    { 
     // Compile error here 
     return echo(getList()); 
    } 

    public static List<Integer> echo(List<Integer> intList) 
    { 
     return intList; 
    } 
} 
+0

謝謝馬特!我想我沒有正確地對這些做減價。 – 2011-04-05 14:40:00

回答

7

前兩種方法中的上下文受賦值轉換使用getList()。同樣的不是對於bindViaMethodInvocation爲真 - 使用表達式作爲方法參數是而不是受分配轉換的影響。

JLS section 15.12.2.8

如果任何方法的類型參數沒有從類型的實際參數推斷出,他們現在推測如下。

  • 如果方法結果發生在將被賦值轉換(第5.2節)爲類型S的上下文中,則令R爲該方法的聲明結果類型,並設R'= R [ T1 = B(T1)... Tn = B(Tn)]其中B(Ti)是前一節中Ti推斷出的類型,或Ti如果沒有推斷出類型。

JLS不清楚爲什麼返回語句計數在這裏。我能找到的最接近的是14.17

帶表達式的return語句必須包含在該聲明爲返回(8.4節),或發生編譯時錯誤值的方法聲明。表達式必須表示某個類型T的變量或值,否則會發生編譯時錯誤。類型T必須是可分配的(第5.2節)到方法的聲明結果類型,否則會發生編譯時錯誤。

(如果第5.2節指出,return語句受到分配轉換這將是很好。)

0

JLS 3#15.12.2.8允許類型推斷在有限的環境中。我將它評爲設計錯誤。表達式的含義應該是上下文無關的,對每個人來說都更容易。

由於getList()的含義因周圍環境而異,這對於Java程序員來說是違反直覺的(前所未有),所以你會發現前兩個編譯器和第三個編譯器都沒有。你並不孤單,這種問題已經反覆提出。他們可以告訴我們RTFS,但需要閱讀規格的人越多,規格就越差。

當然,如果上下文相關的解釋真的有用並且需要,我們必須是實用的。但幾乎沒有證據支持這一點。這種類型推斷是危險的,大多數使用它的代碼都是99%錯誤設計的。目前尚不清楚他們認爲有必要添加這種類型推斷規則。

如果Java泛型被「定義」,即T的值在運行時可用於方法調用,我們可以想象這種類型推斷是安全和有用的。但是,T在運行時不可用,因此getList()調用是上下文無關的,因此無法返回調用站點預期的正確類型。除非有一些保護類型健全性的超文本應用程序邏輯。然後它幾乎不是「靜態打字」。

已經有很多人走得更遠,並要求以下類型的推理:「如果我寫的,我當然知道運行時返回類型是酒吧,因此停止質疑我,愚蠢的編譯器」

Object getFoo(){ .. } 

Bar bar = getFoo(); 

因爲

我尊重那個意見,但你應該選擇一種不同的語言。在靜態和動態類型中,程序員都知道類型,但是靜態類型的要點在於,我們希望明確地寫下源代碼中的類型 - 不是爲了幫助編譯器,而是爲了讓自己受益。 「類型推斷」違反了確切的目標;它只應該被做,如果它不給任何人讀任何類型的真正的代碼的任何神話。不幸的是,Java的類型推斷非常神祕。

相關問題