2016-11-26 55 views
5

我正在使用lambda來實現下面的Java程序中的函數接口。當lambda作爲參數傳遞給泛型方法時,編譯器會標記「不兼容類型」錯誤,因爲它會推斷出lambda表達式實現了接口,該接口將編譯器將lambda參數(「thing」)解釋爲當lambda嘗試將它傳遞給需要類型爲Round的參數的方法(testRound)時,它的類型爲Shape。這個錯誤對我來說很有意義。爲什麼lambda類型推斷失敗,但成功取得等價的方法引用?

但等效的方法參考不會引發錯誤消息。我一直在誤解,一個lambda和一個可以代替lambda的方法引用是可以互換的。在這裏,情況並非如此。

public class Main 
{ 
    public static void main(String... args) 
    { 
     methodB(thing -> Main.testRound(thing)); // incompatible types 
     methodB(Main::testRound);    // no problem here 
    } 

    static <T extends Shape> void methodB(Func<T> function) 
    { 
    } 

    static boolean testRound(Round thing) 
    { 
     return true; 
    } 
} 

interface Func<T> 
{ 
    boolean test(T ob); 
} 

class Shape 
{ 
} 

class Round extends Shape 
{ 
} 

爲什麼當lambda失敗時方法引用成功?

UPDATE

文斯Emigh找到了答案,作爲接受的,下面我已標記。雖然這不是我的問題的一部分,這裏有四個方法可以解決的事實,拉姆達只是推斷爲存在Func<Shape>類型,如果一個人真的堅持使用lambda表達式上:

// Use a type witness. 

Main.<Round>methodB(thing -> testRound(thing)); 

// Make the lambda's argument type explicit. 

methodB((Round thing) -> testRound(thing)); 

// Cast the argument. 

methodB(thing -> testRound((Round)thing)); 

// Store the lambda reference in a Func<Round> variable. 

Func<Round> lambda = thing -> testRound(thing); 
methodB(lambda); 

我沒有看到任何除非人們認爲lambda稍微不密集(也許可讀性更強一點),否則就更喜歡這些方法之一。但是,如果你想要他們,他們就在那裏。

+1

我可以做出的唯一假設(我仍然覺得它是一個拉伸)是:*推論發生在不同的時間*。對於lambda,'thing'被視爲'T extends Shape'(一旦聲明'thing'就被推斷出來),而'testRound'明確需要'Round',導致錯誤。當使用方法引用時,參數被視爲'Round',因爲它是從'Main :: testRound'而不是'(T thing)'推斷的。希望這是有道理的,並且記住這是基於經驗而不是文檔的懷疑論。我正在研究這個問題,希望得到更好的(更正式的)答案 –

+0

@VinceEmigh,我有類似的想法,但有類似的警告。如果您發現任何問題,請告訴我。它確實令人困惑。 –

+0

也許這樣:在第一次調用時,lambda實現''Func ''接口,編譯器從中推斷lambda的參數是'Shape'類型,並且傳遞'Shape 'testRound'。在第二次調用中,實現的參數類型是來自方法簽名的_copied_,這裏是'Round thing'。 (見JLS 15.12。3,「編譯時參數類型是編譯時聲明[。]」的形式參數的類型)的確,這意味着其他方面相同的lambda表達式和成員引用是不可互換的。 –

回答

1

JLS §15.13.2

不同於lambda表達式,方法參考可以是全等與通用功能類型(即,具有輸入參數的函數的類型)。這是因爲lambda表達式需要能夠聲明類型參數,並且沒有語法支持這個;而對於方法引用,則不需要這樣的聲明。

由於沒有指定類型參數,lambda表達式會引發錯誤。這會導致T被編譯爲Shape(如您在文章中所述),因爲沒有任何東西可以幫助推斷參數的類型。

至於方法引用,由於可以從方法的參數中推斷出類型,因此不需要顯式類型參數,如上面JLS語句中所述。

相關問題