简体   繁体   English

不明确的方法调用

[英]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 statingcatch块中给出方法调用的错误说明

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:因为您传递了以下参数:

  • "main" -> String “主要”-> 字符串
  • "Error in main" -> String “主要错误”-> 字符串
  • e -> Exception e -> 异常

now which of your log methods should be called?现在应该调用哪个日志方法?

Both methods would accept these arguments.这两种方法都会接受这些参数。

log method 1日志方法1

static void log(String methodName, Object message, Throwable t)
  • "main" -> String -> matches String methodName "main" -> String -> 匹配String methodName
  • "Error in main" -> String is also an Object -> matches Object message “主错误”-> String也是一个Object -> 匹配Object message
  • e -> Exception -> Exception is a Throwable -> matches Throwable t e -> Exception -> Exception is a Throwable -> 匹配Throwable t

log method 2日志方法2

static void log(String requestId, String method, Object message)
  • "main" -> String -> matches String requestId "main" -> String -> 匹配String requestId
  • "Error in main" -> String -> matches String method “主错误”-> String -> 匹配String method
  • e -> 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.解释器可以将您的StringObject并将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?如果您使用StringStringThrowable调用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更具体的类型,但对于第三个参数, ThrowableObject更具体。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM