Java Ambiguous method when a method has a generic parameter and other method has a generic lambda

huangapple go评论46阅读模式
英文:

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 -&gt; {});
	}

    //replace CharSequence with whatever except Object
	public &lt;E extends CharSequence&gt; void run(E e) {
	}
	public &lt;E extends CharSequence&gt; void run(Consumer&lt;E&gt; 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&#160;and
run(Consumer&lt;CharSequence&gt;) in Main&#160;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 -&gt; {});
	}
	public &lt;E extends ArrayList&gt; void run(E e) {
	}
	public &lt;E extends String&gt; void run(Consumer&lt;E&gt; 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 -&gt; {};
run(charSequence);

o -&gt; {} 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.&lt;CharSequence&gt;run(o -&gt; {});

or

Consumer&lt;CharSequence&gt; consumer = o -&gt; {};
run(consumer);

or (personally less preferable)

run((Consumer&lt;CharSequence&gt;)o -&gt; {});

It could be that the compiler sees E as a type that is both a Consumer&lt;CharSequence&gt; and CharSequence.

class ReallyWeirdCharSequence implements Consumer&lt;CharSequence&gt;, 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 -&gt; {});
	}
	public &lt;E extends Class&gt; void run(E e) {
	}
	public void run(Consumer e) {
	}

Or:

	public void run() {
		run(o -&gt; {});
	}
	public &lt;E extends Class&gt; void run(E e) {
	}
	public &lt;E extends Integer&gt; void run(Consumer&lt;E&gt; 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 -&gt; {}).toString());

and

run((Consumer&lt;CharSequence&gt;)(o -&gt; {}));

huangapple
  • 本文由 发表于 2020年7月22日 01:32:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/63019945.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定