【问题标题】:Overload resolution, which method is called重载解析,调用哪个方法
【发布时间】:2020-05-08 18:01:08
【问题描述】:

假设我有一个 ComponentBase 班级,他是 ObjectContextDecorator 的孩子和 ObjectContext 的孙子。

public class ComponentBase extends ObjectContextDecorator {
}

public class ObjectContextDecorator extends ObjectContext {

    public void set(String objectTypePath, String characteristicName, Object value) {
        //...
    }
}

public class ObjectContext {
    public void set(String characteristicName, Object value, boolean forced) {
       //...
    }
}

ObjectContextDecoratorObjectContext 上的 set 方法非常相似。考虑这个示例代码:

ComponentBase base = new ComponentBase();
base.set(""OTM4E_EFFLEVEL"", ""IE1 / STD"", true);

这两种方法的签名都适合被正确调用的方法。我无法更改方法的签名,因为它不是我的代码。

编译器如何知道我打算调用哪个方法?

我知道在 IDE 上您可以指出您实际打算调用的方法,但在这种情况下,我使用类加载器来加载一个类,该类具有包含示例代码的方法。

【问题讨论】:

  • 它们相似,但不同——如果您传入String, String, boolean,则不会有歧义。将调用最具体的方法。这一切都在 JLS 中。
  • 哪一个是最具体的?据我了解,它们都同样具体。
  • 也就是说,即使编译器和 IDE 可以确定调用的是哪一个,但规则很复杂,人类很难弄清楚。所以我会重命名其中一种方法以使其显而易见。
  • 旁注,java 字符串只使用一个双引号(如"this"

标签: java overloading method-signature


【解决方案1】:

编译器如何知道我打算调用哪个方法?

它检查参数并根据JLS §15.2 描述的规则确定哪个更具体

在你的情况下,调用:

base.set("OTM4E_EFFLEVEL", "IE1 / STD", true)

参数是String,String,boolean

与第一类匹配(为简洁起见更改了参数名称

public class ObjectContext {
    public void set(String s, Object o, boolean b){
       //...
    }
}

第二个类没有被调用,因为第三个参数是Object

public class ObjectContextDecorator extends ObjectContext {

    public void set(String s, String ss, Object thisOneRightHere) {
        //...
    }
}

虽然布尔值true 可以匹配,如果它是自动装箱的,但第一个更具体。此处适用的规则是:

第一阶段 (§15.12.2.2) 执行重载解析不允许装箱或拆箱转换

但是,例如,如果您在签名中使用对象包装器Boolean

public class ObjectContext {
    public void set(String s, Object o, Boolean b){ //<-- third param changed from boolean to Boolean
       //...
    }
}

然后它们都会匹配,编译器会通过以下消息通知您:

> A.java:25: error: reference to set is ambiguous
>     base.set("OTM4E_EFFLEVEL", "IE1 / STD", true);
>         ^   both method set(String,Object,Boolean) in ObjectContext and method set(String,String,Object) in ObjectContextDecorator match

但在您的示例中并非如此。

【讨论】:

    【解决方案2】:

    这一切都在JLS §15.2 方法调用表达式中进行了解释。它告诉您如何选择正确的调用方法。请注意,这并不总是成功。

    在您的特定情况下,这两个方法是彼此的重载,因此第 15.2.2 节“编译时步骤 2:确定方法签名”适用 - 要调用的重载是在编译时确定的。此步骤进一步分为 3 个阶段。

    第一阶段(第 15.12.2.2 节)执行重载决议,不允许装箱或拆箱转换,或使用可变的 arity 方法调用。如果在此阶段没有找到适用的方法,则处理继续到第二阶段。

    在第一阶段,编译器尝试在不允许装箱转换的情况下找到适用的方法。在您的情况下,要调用采用Object 的重载,需要进行装箱转换以将boolean true 转换为Object 类型,以便在第一阶段不会选择重载。

    如果未找到通过严格调用适用的方法,则搜索适用方法将继续第 2 阶段(第 15.12.2.3 节)。

    否则,将在严格调用适用的方法中选择最具体的方法(第 15.12.2.5 节)。

    嗯,我们已经找到了一种方法,所以我们只选择那个方法。没有歧义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 2018-10-05
      • 2010-10-17
      • 2019-09-17
      • 1970-01-01
      相关资源
      最近更新 更多