【问题标题】:Java method overloading + double dispatchJava方法重载+双重分派
【发布时间】:2011-02-17 03:53:22
【问题描述】:

谁能详细解释在我的测试代码中使用Child 实例时调用重载方法print(Parent parent) 的原因?

这里涉及到 Java 中虚拟方法或方法重载/解析的任何特性? 任何直接参考 Java 语言规范? 哪个术语描述了这种行为? 非常感谢。

public class InheritancePlay {

    public static class Parent {        
        public void doJob(Worker worker) {
            System.out.println("this is " + this.getClass().getName());

            worker.print(this);
        }
    }

    public static class Child extends Parent {
    }

    public static class Worker {
        public void print(Parent parent) {
            System.out.println("Why this method resolution happens?");
        }

        public void print(Child child) {
            System.out.println("This is not called");
        }
    }

    public static void main(String[] args) {
        Child child = new Child();
        Worker worker = new Worker();

        child.doJob(worker);
    }
}

【问题讨论】:

    标签: java oop overloading double-dispatch


    【解决方案1】:

    §8.4.9 Overloading 中的 JLS 状态:

    1. 调用方法时(第 15.12 节),在编译时使用实际参数(以及任何显式类型参数)和编译时参数类型来确定将被调用的方法的签名(第 15.12.2 节)。
    2. 如果要调用的方法是实例方法,则要调用的实际方法将在运行时使用动态方法查找 (§15.12.4) 确定。

    所以在你的情况下:

    1. 方法参数 (this) 是编译时类型 Parent,因此会调用方法 print(Parent)
    2. 如果 Worker 类是子类并且子类将覆盖该方法,并且 worker 实例属于该子类,则将调用被覆盖的方法。

    Java 中不存在双重分派。你必须模拟它,例如通过使用Visitor Pattern。在这种模式下,基本上,每个子类都实现了一个accept 方法并以this 作为参数调用访问者,而this 具有该子类的编译时类型,因此使用了所需的方法重载。

    【讨论】:

    • 克里斯蒂安,感谢您详尽的回答!所以我们在这里处理运行时 VS 编译时类型的事情。我会深入探讨这个话题。 (这里提到了双重调度,因为我在学习访问者模式时遇到了这个问题:))。最大
    【解决方案2】:

    原因是doJobParent中实现,在Child中没有重载。它将this 传递给worker 的print 方法,因为this 属于Parent 类型,所以将调用Worker::print(Parent) 方法。

    为了让Worker::print(Parent)被调用,你需要在Child中重载doJob

    public static class Child extends Parent {
        public void doJob(Worker worker) {
            System.out.println("from Child: this is " + this.getClass().getName());
    
            worker.print(this);
        }
    }
    

    在上面的代码中this.getClass() 中的Child 等价于Child.class

    【讨论】:

    • rsp,感谢您指出这一点,但我知道在 Child 中覆盖 doJob 会使 prog 工作。问题是我不明白为什么:)。让我们看一下初始代码。当我们将 Child 对象传递给 Parent.doJob 时 Parent.doJob 内部的 Java 反射证明我们处理的是 Child 类型的 this,那么为什么重载方法的解析失败?
    • @Max,在方法内部this的类型始终是该方法所在类的类型。编译器无法知道该类将来会继承自,它必须使用当时所拥有的知识。 this.getClass() 是运行时信息,不是编译时间。
    猜你喜欢
    • 2012-04-03
    • 1970-01-01
    • 2016-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多