【问题标题】:Super keyword in Java, interesting behavior, please explainJava中的超级关键字,有趣的行为,请解释
【发布时间】:2014-02-26 17:22:57
【问题描述】:

假设我们有以下代码:

class A {

    public void doLogic() {
        System.out.println("doLogic from A");
    }
}

class B extends A {

    @Override
    public void doLogic() {
        System.out.println("doLogic from B");
    }

    public void doDifferentLogic() {
        System.out.println("doDifferentLogic from B");
        super.doLogic();
    }
}

class C extends B {

    @Override
    public void doLogic() {
        System.out.println("doLogic from C");
    }
}

public class Test {

    public static void main(String[] args) {
        C c = new C();
        c.doDifferentLogic();
    }
}

当我们执行这段代码时,预期的行为如下: 由于 c 持有对 C 类对象的引用,因此当您调用 c.doDifferentLogic() 方法时,JVM 会在 C 类中搜索该方法,并且由于找不到它,它开始查看继承树。正如预期的那样,doDifferentLogic() 方法在超类中找到并执行。但是构造 super.doLogic() 应该从 C 类型的当前引用“Point of View”中查找。因此 C 的超类应该是 B,而是调用顶级类 A 中的方法。

如果您删除 super 关键字,或将其替换为 this 关键字(与“this”是隐式的相同),您将获得预期的多态行为,并调用 C 类中的 doLogic() .

所以我的问题是: 应该调用super.doLogic()this.super.doLogic()(2),而不是static.super.doLogic()(1)?

两者都是无效的结构,他们在这里只是为了更好地解释自己。

(1) 或者换句话说 - 从对当前对象 c 的引用中,获取当前对象的超类并调用 doLogic() 方法而不是 (2)从这个类中获取超类并调用它的doLogic()方法?

【问题讨论】:

  • 是否有这样的情况会如所描述的那样发生,或者您为什么希望它以这种方式发生?它不会冒着意外/隐式修改 B.doDifferentLogic() 的实现的风险吗?
  • 如果我理解你的问题,你问的是从你的类的其他公共方法中调用公共方法是否是糟糕的设计 - 是的。如果你想保证 doDifferentLogic() 在子类中的行为总是相同的,你应该将 doLogic() 设为私有,如果你想让这两个方法都公开,你应该从公共方法中调用私有方法,如下所示:@Override public void doLogic() { doPrivateLogic() } private void doPrivateLogic() { System.out.println("doLogic from B"); }

标签: java inheritance super


【解决方案1】:

在 Java 中,super 关键字总是指使用该关键字的类型的超类,而不是调用该方法的对象的动态类型的超类。换句话说,super 是静态解析的,而不是动态解析的。这意味着在B 类的上下文中,super 关键字始终引用A 类,无论是否使用C 对象作为接收者来执行B 方法。据我所知,没有办法在不使用反射的情况下动态确定超类类型并使用其方法。

希望这会有所帮助!

【讨论】:

  • 只是为了补充这个出色的答案,当引用基类上定义的方法时,调用 super.doLogic() 将沿着继承层次结构向上走,直到找到 doLogic() 方法的具体实现执行。
  • 这里是相关的 JLS 部分:Section 15.11.2
  • 只是想澄清一下 Brian 的评论,编译器不会静态执行对超级实现的查找。它是由 VM 执行的动态查找,始终从具有 super 调用的方法所属的类的直接父类开始。 This behaviour was changed in Java 1.1.
【解决方案2】:

这里是JLS defines this specifically:

  • 如果表单是 super 。 NonWildTypeArgumentsopt Identifier,那么方法的名字就是Identifier,而要搜索的类是声明中包含的类的超类方法调用。

所以 Java 认为 super 是指包含 super.method 调用的类的超类,而不是实际的运行时类型。

【讨论】:

    【解决方案3】:

    每当执行 classB 的 super.doLogic() 时,它总是会引用 classB 的超类的 doLogic() 方法,在这种情况下是 classA。这是所需的行为,因此控制不会在同一方法内的类之间不断传递。这是类上下文的概念。一旦你在一个类的上下文中,你将必须遵循该类建立的规则,而不是在不同的上下文之间传递控制。

    【讨论】:

    • 但如果这是真的,调用 this.doLogic();在B类中,当对象来自C类时,应该调用B类中的方法,而不是调用C类中的方法。实际上是跳转到B类,找到那里的方法,然后从引用重新开始?跨度>
    猜你喜欢
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多