我試圖理解為什么編譯器無法決議bar方法呼叫。我希望bar(Xyz::new)始終選擇bar(Supplier)為bar(T extends Xyz)不能匹配,由于上界Xyz。
public <T extends Xyz> void foo(T s) {}
public <T extends Xyz> void bar(T s) {}
public <T extends Xyz> void bar(Supplier<T> s) {}
public void example() {
foo(Xyz::new); // not valid (does not extend Xyz)
bar((Supplier<Xyz>) Xyz::new); // valid (explicitly a Supplier)
bar(Xyz::new); // ambiguous - but only one method is valid?
}
public static class Xyz {}
如果bar(T)不適用,即使單獨使用(如 所示foo(T)),那么唯一的選擇肯定是bar(Supplier)使其成為明確的多載。
為什么bar呼叫是模棱兩可的,尤其是當foo和bar(T)呼叫本身不是有效的解決方案時?
上述代碼的可運行示例:https : //www.jdoodle.com/ia/kqP
uj5u.com熱心網友回復:
你是對的,一個更聰明的編譯器應該能夠明確地解決這個問題。
Java 決議方法呼叫的方式很復雜。它是由 JLS 定義的,我用 7500 字純粹是為了確定如何決議一個方法。粘貼到文本編輯器中,它有 15 頁。
一般的做法是:
- 編譯時步驟 1:確定要搜索的型別(這里沒有問題)
- 編譯時步驟 2:確定方法簽名
- 確定可能適用的方法
- 階段 1:確定適用于嚴格呼叫的匹配 Arity 方法
- 階段 2:確定適用于松散呼叫的匹配 Arity 方法
- 階段 3:確定變數呼叫適用的方法
- 選擇最具體的方法
- 方法呼叫型別
- 編譯時步驟 3:選擇的方法是否合適?
我不了解接近所有細節的任何地方以及它與您的具體情況的關系。如果您想深入了解它,那么我已經鏈接了完整的規范。希望這個解釋足以滿足您的目的:
歧義是在步驟 2.6 確定的,但在步驟 3 中仍有進一步的適當性檢查。您的foo方法必須在步驟 3 中失敗。您的bar方法永遠不會成功,因為編譯器仍然認為這兩種方法都是有效的可能性。人類可以確定不適當性解決了歧義,但這不是編譯器做事的順序。我只能推測原因 - 性能可能是一個因素。
您的代碼在泛型、多載和方法參考的交叉點上運行,這三者都是在不同時間引入的;編譯器會掙扎對我來說并不奇怪。
uj5u.com熱心網友回復:
您的問題主要是型別推斷問題,即模棱兩可的方法問題:
public <T extends Xyz> void bar(T s) {} // bar(Xyz)
public void bar(String s) {}
public <T extends Zyx> void bar(T s) {} // bar(Zyx)
public <T extends Xyz> void bar(Supplier<T> s) {}
public static class Xyz {}
public static class Zyx {}
如果您使用:
bar(new Xyz()); // ok
bar("a"); // ok
bar(new Zyx()); // ok
bar((Supplier<Xyz>) Xyz::new); // ok
bar(Xyz::new); // ambiguous
您收到此錯誤(在 Java 17 中嘗試過),這與 lambda 無關,而是與型別有關T:無法推斷型別變數 T
both method <T#1>bar(T#1) in Example and method <T#2>bar(Supplier<T#2>) in Example match
where T#1,T#2 are type-variables:
T#1 extends Zyx declared in method <T#1>bar(T#1)
T#2 extends Xyz declared in method <T#2>bar(Supplier<T#2>)
Example.java:18: error: incompatible types: cannot infer type-variable(s) T
Java 不夠聰明,無法找到具體型別 T 是這種情況,您必須幫助它:
Example.<Xyz>bar(Xyz::new);
我試圖研究由邁克爾回答驅動的 JLS,應該更好地回答您的問題的部分是18.5.1。呼叫適用性推斷。
我在 Java 7 和 Collections 中經常發生同樣型別的錯誤:
public static <T extends Zyx> void bar(java.util.List<T> s) {} // bar(Zyx)
public static <T extends Zyx> void bar(T s) {} // bar(List)
bar(new Zyx());
bar(java.util.Collections.emptyList());
更糟糕的是 Eclipse 沒有問題,而 javac 失敗了。
我想在 lambdas 的情況下,編譯器不會從“Xyz”推斷出型別 T。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/370263.html
上一篇:c#struct通過泛型回圈參考
