Java compiler: How can two methods with the same name and different signatures match a method call?

浪子不回头ぞ 提交于 2019-11-29 04:01:41

According to the JLS §15.12.2.2:

An argument expression is considered pertinent to applicability for a potentially applicable method m unless it has one of the following forms:

  • An implicitly typed lambda expression1.
  • An inexact method reference expression2.
  • [...]

Therefore:

verify("bar", tokens.get("foo", e -> String.valueOf(e)));

an implicitly typed lambda expression e -> String.valueOf(e) is skipped from the applicability check during overload resolution - both verify(...) methods become applicable - hence the ambiguity.

In comparison, here are some examples that will work, because the types are specified explicitly:

verify("bar", tokens.get("foo", (Function<Object, String>) e -> String.valueOf(e)));

verify("bar", tokens.get("foo", (Function<Object, String>) String::valueOf));

1 - An implicitly typed lambda expression is a lambda expression, where the types of all its formal parameters are inferred.
2 - An inexact method reference - one with multiple overloads.

There are multiple implementations of String.valueOf(...) with different arguments. The compiler does not know which one you want to call. The compiler is not capable of seeing that all the possible methods actually return a String and therefore it does not really matter which method is called. Since the compiler does not know what the return type will be it cannot infer a proper Function<...,...> as the type of the expression and it therefore cannot understand wether you will have a Function or something else at hand and therefore cannot tell if you want to call the get method with a Function or a Class.


If you instead of String::valueOf use e -> String.valueOf(e) then the compiler can infer a little more but it will still not understand that you will always return a String and will therefore interpret it as Function<Object, Object> which your verify method then has a problem with.


e -> e.toString I do not understand fully, I do not see why the compiler is incapable of inferring String as a return type here. It infers Object and does the exact same thing as in the previous case. If you split the operation into

String s = tokens.get("baz", e -> e.toString());
verify("bat", s);  // line 21

then it works because the compiler can infer the generic R from the type of s. The same way it works by explicitly specifying R:

verify("bat", tokens.<String>get("baz", e -> e.toString()));  // line 21

String.class the compiler easily understands that you want to call the get(Class) method.


Object::toString makes sense to work since the compiler knows this will be a Function<Object, String>.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!