英文:
Java Ambiguous method when a method has a generic parameter and other method has a generic lambda
问题
考虑以下内容:
public void run() {
run(o -> {});
}
// 用除了Object以外的任何东西替换CharSequence
public <E extends CharSequence> void run(E e) {
}
public <E extends CharSequence> void run(Consumer<E> e){
}
编译器报错 java reference to run is ambiguous
,即使传递的参数明显是 lambda 表达式,而且只有两个方法中的一个是 lambda 方法。
问题是为什么会发生这种情况?这是一个 bug 吗?要实现相同的行为,应该用什么签名替换?
额外信息:
IntelliJ 提示(类名Main
无关):
Ambiguous method call. Both
run(CharSequence) in Main和
run(Consumer<CharSequence>) in Main匹配
注意:
用除了java.lang.Object
以外的任何东西替换CharSequence
都会产生相同的问题!
还要注意:
即使这两个泛型不同且彼此无关,只要它们不是java.lang.Object
,这也不会编译通过:
public void run() {
run(o -> {});
}
public <E extends ArrayList> void run(E e) {
}
public <E extends String> void run(Consumer<E> e){
}
英文:
Consider the following:
public void run() {
run(o -> {});
}
//replace CharSequence with whatever except Object
public <E extends CharSequence> void run(E e) {
}
public <E extends CharSequence> void run(Consumer<E> e){
}
The compiler complains about java reference to run is ambiguous
even that the parameter passed is clearly a lambda and only one of the two methods is lambda
The question is why this is happening? Is it a bug? what should the signature be replaced with to achieve the same behavior?
Extra:
IntelliJ says (the class Main
does not matter):
Ambiguous method call. Both
run(CharSequence) in Main and
run(Consumer<CharSequence>) in Main match
Note:
replacing CharSequence
with ANYTHING except java.lang.Object
will produce the same issue!
Also Note:
It does not matter even if the two generics are different and don't relate to each other! As long as they are not java.lang.Object
.
So, this will not compile ether:
public void run() {
run(o -> {});
}
public <E extends ArrayList> void run(E e) {
}
public <E extends String> void run(Consumer<E> e){
}
答案1
得分: 1
没有一个CharSequence可以声明为lambda表达式。
看一下。
interface WeirdCharSequence extends CharSequence {
default int length() { return 0; }
default char charAt(int index) { return 0; }
default CharSequence subSequence(int start, int end) { return null; }
void accept(CharSequence i);
}
证明它可以:
WeirdCharSequence charSequence = o -> {};
run(charSequence);
o -> {}
可能有很多含义,没有足够的上下文来确定lambda表达式到底代表什么。你可以通过显式声明所需的类型来帮助编译器。
this.<CharSequence>run(o -> {});
或者
Consumer<CharSequence> consumer = o -> {};
run(consumer);
或者(个人不太推荐的方式)
run((Consumer<CharSequence>)o -> {});
也可能是编译器将 E
视为既是 Consumer<CharSequence>
又是 CharSequence
的类型。
class ReallyWeirdCharSequence implements Consumer<CharSequence>, CharSequence {
public void accept(CharSequence i) { }
public int length() { return 0; }
public char charAt(int index) { return 0; }
public CharSequence subSequence(int start, int end) { return null; }
}
英文:
> Nor a CharSequence can be declared as a lambda
Have a look.
interface WeirdCharSequence extends CharSequence {
default int length() { return 0; }
default char charAt(int index) { return 0; }
default CharSequence subSequence(int start, int end) { return null; }
void accept(CharSequence i);
}
proves it can
WeirdCharSequence charSequence = o -> {};
run(charSequence);
o -> {}
could mean many things, and there is not enough context to figure out what exactly the lambda represents. You can always help the compiler out by explicitly declaring the desired type.
this.<CharSequence>run(o -> {});
or
Consumer<CharSequence> consumer = o -> {};
run(consumer);
or (personally less preferable)
run((Consumer<CharSequence>)o -> {});
It could be that the compiler sees E
as a type that is both a Consumer<CharSequence>
and CharSequence
.
class ReallyWeirdCharSequence implements Consumer<CharSequence>, CharSequence {
public void accept(CharSequence i) { }
public int length() { return 0; }
public char charAt(int index) { return 0; }
public CharSequence subSequence(int start, int end) { return null; }
}
答案2
得分: 0
经过一些调查,我意识到这是Java语言中的一个错误。
请自行尝试以下代码:
public void run() {
run(o -> {});
}
public <E extends Class> void run(E e) {
}
public void run(Consumer e) {
}
或者:
public void run() {
run(o -> {});
}
public <E extends Class> void run(E e) {
}
public <E extends Integer> void run(Consumer<E> e) {
}
尽管这两个函数的参数没有相关性,但Java编译器在某种程度上认为它们有关联。
总之,创建一个带有通用参数的方法将使该方法有效地作为接受Lambda表达式的方法。
英文:
After some investigations, I realized that it is a bug in the java language.
Try that your self:
public void run() {
run(o -> {});
}
public <E extends Class> void run(E e) {
}
public void run(Consumer e) {
}
Or:
public void run() {
run(o -> {});
}
public <E extends Class> void run(E e) {
}
public <E extends Integer> void run(Consumer<E> e) {
}
Even that the parameters of both functions don't relate to each other. Somehow the java compiler thinks that they are.
In conclusion, creating a method with a generic parameter will make the method valid as a method that accepts lambda expression.
答案3
得分: -2
猜测一下,但一切都有一个 toString 方法,隐式地通过编译器隐式调用它来将事物转换为字符串是很常见的。字符串当然是 CharSequence 的继承者。因此编译器可能会抱怨无法决定以下两者之间:
run((CharSequence)(o -> {}).toString());
和
run((Consumer<CharSequence>)(o -> {}));
英文:
Guessing now, but everything has a toString method and it's kinda common to implicitly convert things to strings by having the compiler implicitly call it for you. String is of course an inheritor of CharSequence. So the compiler could be complaining that it can't decide between:
run((CharSequence)(o -> {}).toString());
and
run((Consumer<CharSequence>)(o -> {}));
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论