为什么这个 Java 泛型方法调用在分开时只有一个方法有效时不明确?
Why is this Java generic method call ambiguous when only one method is valid when separate?
我想了解为什么编译器无法解析 bar
方法调用。我希望 bar(Xyz::new)
总是 select 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
你是对的,更聪明的编译器应该能够明确地解决这个问题。
Java 解析方法调用的方式复杂。就是defined by the JLS,我写了7500字纯粹是为了确定一个方法怎么解析。粘贴到文本编辑器中,它是 15 页。
一般做法是:
- 编译时步骤 1:确定要搜索的类型(此处没有问题)
- 编译时步骤 2:确定方法签名
- 确定潜在适用的方法
- 第 1 阶段:识别严格调用适用的匹配 Arity 方法
- 第 2 阶段:识别适用于松散调用的匹配 Arity 方法
- 第 3 阶段:确定变量 Arity 调用适用的方法
- 选择最具体的方法
- 方法调用类型
- 编译时第 3 步:选择的方法是否合适?
我不了解所有细节及其与您的具体案例的关系。如果您想深入研究它,那么我已经链接了完整的规范。希望这个解释足以满足您的目的:
歧义性 在第 2.6 步确定,但在第 3 步还有进一步的 适当性 检查。您的 foo
方法必须在第 3 步失败。您的 bar
方法永远不会走那么远,因为编译器仍然认为这两种方法都是有效的可能性。人可以确定不适当性解决了歧义,但这并不是编译器做事的顺序。我只能推测原因 - 性能可能是一个因素。
您的代码在泛型、重载和方法引用的交叉点运行,这三者都是在不同的时间引入的;编译器会遇到困难对我来说并不奇怪。
你的问题主要是类型推断的问题,而不是方法不明确的问题:
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
: cannot infer type-variable(s) 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);
我试图查看由 Michael 回答驱动的 JLS,而应该更好地回答您问题的部分是 18.5.1. Invocation Applicability Inference。
我经常遇到与 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 却失败了。
我想在 lambda 的情况下,编译器不会从“Xyz”推断类型 T。
我想了解为什么编译器无法解析 bar
方法调用。我希望 bar(Xyz::new)
总是 select 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
你是对的,更聪明的编译器应该能够明确地解决这个问题。
Java 解析方法调用的方式复杂。就是defined by the JLS,我写了7500字纯粹是为了确定一个方法怎么解析。粘贴到文本编辑器中,它是 15 页。
一般做法是:
- 编译时步骤 1:确定要搜索的类型(此处没有问题)
- 编译时步骤 2:确定方法签名
- 确定潜在适用的方法
- 第 1 阶段:识别严格调用适用的匹配 Arity 方法
- 第 2 阶段:识别适用于松散调用的匹配 Arity 方法
- 第 3 阶段:确定变量 Arity 调用适用的方法
- 选择最具体的方法
- 方法调用类型
- 编译时第 3 步:选择的方法是否合适?
我不了解所有细节及其与您的具体案例的关系。如果您想深入研究它,那么我已经链接了完整的规范。希望这个解释足以满足您的目的:
歧义性 在第 2.6 步确定,但在第 3 步还有进一步的 适当性 检查。您的 foo
方法必须在第 3 步失败。您的 bar
方法永远不会走那么远,因为编译器仍然认为这两种方法都是有效的可能性。人可以确定不适当性解决了歧义,但这并不是编译器做事的顺序。我只能推测原因 - 性能可能是一个因素。
您的代码在泛型、重载和方法引用的交叉点运行,这三者都是在不同的时间引入的;编译器会遇到困难对我来说并不奇怪。
你的问题主要是类型推断的问题,而不是方法不明确的问题:
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
: cannot infer type-variable(s) 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);
我试图查看由 Michael 回答驱动的 JLS,而应该更好地回答您问题的部分是 18.5.1. Invocation Applicability Inference。
我经常遇到与 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 却失败了。
我想在 lambda 的情况下,编译器不会从“Xyz”推断类型 T。