[英]Ambiguous method call
The below code block下面的代码块
package com.example;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Test {
private static final Logger LOGGER = LogManager.getLogger(Test.class);
public static void main(String[] args) {
try {
log("1234", "main", "try");
} catch (Exception e) {
log("main", "Error in main",e);
}
}
static void log(String methodName, Object message, Throwable t) {
LOGGER.error("[Method Name :" + methodName + "] [Message :" + message + "]", t);
}
static void log(String requestId, String method, Object message) {
LOGGER.error("[RequestId :" + requestId + "]" + "[Method Name :" + method + "] [Message :" + message + "]");
}
}
gives an error for the method call in the catch
block stating在catch
块中给出方法调用的错误说明
error: reference to log is ambiguous log("main", "Error in main",e);错误:对日志的引用不明确 log("main", "Error in main",e); ^ both method log(String,Object,Throwable) in Test and method log(String,String,Object) in Test match ^ 测试中的方法 log(String,Object,Throwable) 和测试中的方法 log(String,String,Object) 都匹配
I'm not able to understand it.我无法理解。 I went through lot of questions here but most of them either have varargs or generics involved.我在这里遇到了很多问题,但其中大多数都涉及可变参数或泛型。 Couldn't determine what's wrong with the above code though.虽然无法确定上面的代码有什么问题。 Going through the JSL unable to determine what rule am I violating.通过 JSL 无法确定我违反了什么规则。 Any help in understanding this would be great.任何帮助理解这一点都会很棒。
I know some questions talked about我知道一些问题被谈到
Renaming the methods重命名方法
Explicit type-casting for parameters参数的显式类型转换
as solutions to make it clear for the compiler which method to pickup.作为解决方案,让编译器明确选择哪种方法。 But I'm looking forward to understand why the overloaded methods don't work here.但我很期待了解为什么重载方法在这里不起作用。 Also if type-casted the second parameter to Object
the code works fine, But why?此外,如果将第二个参数类型转换为Object
代码工作正常,但为什么呢?
Some question did refer to type inference improved in Java 8 than 7, so to clarify I'm running this on Java 8.一些问题确实提到了 Java 8 中改进的类型推断而不是 7,所以为了澄清我在 Java 8 上运行它。
Doesn't appear to be anything to do with generics.似乎与泛型没有任何关系。
You have a method call你有一个方法调用
log("main", "Error in main",e);
and the compiler doesn't know if you're trying to call并且编译器不知道您是否要调用
log(String, Object, Throwable)
or或者
log(String, String, Object)
since both would match and neither is more specific than the other.因为两者都会匹配,并且没有一个比另一个更具体。
To make it clear, you could cast:为了清楚起见,您可以投射:
log("main", (Object) "Error in main", e);
if you want the first, or如果你想要第一个,或者
log("main", "Error in main", (Object) e);
if you want the second.如果你想要第二个。
Or write a log
method more precisely matching the arguments you intend to pass to it.或者编写一个log
方法,更精确地匹配您打算传递给它的参数。
You have two log methods and both of them would match the call log("main", "Error in main",e);
您有两个日志方法,它们都将匹配调用log("main", "Error in main",e);
Because you pass the following arguments:因为您传递了以下参数:
now which of your log methods should be called?现在应该调用哪个日志方法?
Both methods would accept these arguments.这两种方法都会接受这些参数。
static void log(String methodName, Object message, Throwable t)
String
-> matches String methodName
"main" -> String
-> 匹配String methodName
String
is also an Object
-> matches Object message
“主错误”-> String
也是一个Object
-> 匹配Object message
Exception
-> Exception
is a Throwable
-> matches Throwable t
e -> Exception
-> Exception
is a Throwable
-> 匹配Throwable t
static void log(String requestId, String method, Object message)
String
-> matches String requestId
"main" -> String
-> 匹配String requestId
String
-> matches String method
“主错误”-> String
-> 匹配String method
Exception
-> Exception
is an Object
-> matches Object message
e -> Exception
-> Exception
是一个Object
-> 匹配Object message
The ambiguity is arising because the third parameter Exception
is both a Throwable
and an Object
.产生歧义是因为第三个参数Exception
既是Throwable
又是Object
。 Use this version for the second logging method:将此版本用于第二种日志记录方法:
static void log(String requestId, String method, String message) {
LOGGER.error("[RequestId :" + requestId + "]" + "[Method Name :" + method + "] [Message :" + message + "]");
}
Now the following two calls are not ambiguous:现在以下两个调用没有歧义:
log("1234", "main", "try");
log("main", "Error in main", e);
The Error Msg states, that there are two Methods that could solve your request log(String,Object,Throwable)
and log(String,String,Object)
.错误消息指出,有两种方法可以解决您的请求log(String,Object,Throwable)
和log(String,String,Object)
。 The Interpreter could either cast your String
to Object
and keep the Exception
a Throwable
or it could cast the Throwable
to an Object
and keep the String
as it is.解释器可以将您的String
为Object
并将Exception
保持为一个Throwable
或者它可以将Throwable
为一个Object
并保持String
原样。 Both possibilities require one cast, so it throws the exception instead of guessing which variant you would prefer.两种可能性都需要一次转换,因此它会抛出异常而不是猜测您更喜欢哪种变体。
If you call log
with a String
, a String
and a Throwable
, how would you solve the ambiguity?如果您使用String
、 String
和Throwable
调用log
,您将如何解决歧义? There is no preference regarding the order of arguments... So Java is both able to "promote" the String to Object and call the first one, or "promote" the Throwable to an Object and chose the second one, with no preference, thus the ambiguity.没有关于参数顺序的偏好......所以Java既可以将字符串“提升”为对象并调用第一个,也可以将Throwable“提升”为对象并选择第二个,没有任何偏好,因此,歧义。
If you enforce the type of the second argument to Object then of course the compiler will have no choice, it looks to a method whose signature begin with (String,Object,
thus the first one.如果您将第二个参数的类型强制为 Object ,那么编译器当然别无选择,它会查找签名以(String,Object,
因此是第一个.
Your problem is that you are overloading the function by contravariance, one parameter by generalizing the type, and another parameter by specializing the type.您的问题是您通过逆变重载函数,通过泛化类型来重载一个参数,通过专门化类型来重载另一个参数。 There is no solution.没有解决办法。 Prefer not doing it...宁愿不做...
When you call log
with the argument types (String,String,Throwable)
, both methods当您使用参数类型(String,String,Throwable)
调用log
时,两种方法
static void log(String methodName, Object message, Throwable t)
static void log(String requestId, String method, Object message)
are applicable, but neither is more specific than the other.是适用的,但没有一个比另一个更具体。 For the second argument, String
is a more specific type than Object
, but for the third, Throwable
is more specific than Object
.对于第二个参数, String
是比Object
更具体的类型,但对于第三个参数, Throwable
比Object
更具体。
You can solve this by adding another overload您可以通过添加另一个重载来解决此问题
static void log(String methodName, String message, Throwable t) {
log(methodName, (Object)message, t);
}
as then, this new overload is more specific than the others and will be selected when invoking log
with (String,String,Throwable)
.因此,这个新的重载比其他重载更具体,并且在使用(String,String,Throwable)
调用log
时将被选中。
An alternative is to make the second method more versatile by changing it to另一种方法是通过将第二种方法更改为
static void log(String requestId, String method, Object... message) {
LOGGER.error("[RequestId :" + requestId + "]" + "[Method Name :" + method
+ "] [Message :" + (message.length == 1? message[0]: Arrays.toString(message)) + "]");
}
Now it accepts multiple message arguments, but will behave the same as before when being called with only one argument.现在它接受多个消息参数,但当只使用一个参数调用时,其行为与以前相同。 Since varargs methods have less precedence when multiple methods are applicable, calling log
with (String,String,Throwable)
will select the first method which has (String,Object,Throwable)
parameter types.由于可变参数方法在多个方法适用时优先级较低,因此使用(String,String,Throwable)
调用log
将选择具有(String,Object,Throwable)
参数类型的第一个方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.