【问题标题】:Java inconsistent inheritance mechanics?Java不一致的继承机制?
【发布时间】:2014-02-17 18:18:18
【问题描述】:

我想我们已经在Inheritance in Java simple clarificationInheritance in Java simple clarification的这篇文章中讨论了这个问题

但由于这里的示例有点简单,而且我想澄清的点不同,我会试一试。

首先讨论两个类:

public class SuperClass{
    SuperClass() {
        foo();
    }

    public void foo() {
        System.out.println("Super.foo()");
    }

    public static void main(String[] args) {
            SuperClass tn = new DerivedClass();
            tn.foo();
    }
}

public class DerivedClass extends SuperClass{
    String i;

    TrickyB() {
        i = "aksjhdf";
    }

    public void foo() {
        System.out.println("In derived.foo() --> " + i);
    }
}

我(至少我认为)理解多态性的概念,并且我知道为什么在调用时会调用DerivedClass.foo()

new DerivedClass();

我在这里看到了不一致:

在我们调用 DerivedClass 的 c´tor 时,SuperClass 的 c´tor 被隐式​​调用(可以说是 Derived c´tor 的第一行)。

所以在 Super c´tor 中,DerivedClass 没有完全初始化,这使得使用这个类毫无用处。 这一点反映在这个程序的输出中

In derived.foo() --> null
In derived.foo() --> aksjhdf

第一行反映了我的困惑:

为什么会调用DerivedClass.foo()?对象还没有准备好,所以用它做任何事情在我看来都是无稽之谈。

谁能给我解释一下原因。我认为这绝对违反直觉。

顺便说一句:我本来希望调用SuperClass.foo(),因为正如我所说,使用“未就绪”对象没有任何意义。

另一方面:正如我所想。这对我来说也没有任何意义,当我在 SuperClass 的 c´tor 中时,DerivedClass.foo() 被调用了!

在我的情况下,我该如何称呼SuperClass.foo()

【问题讨论】:

    标签: java oop inheritance


    【解决方案1】:

    为什么要调用 DerivedClass.foo()?对象还没有准备好,所以用它做任何事情在我看来都是无稽之谈。

    没有。对象已经创建。构造函数不创建对象,它只是初始化它们。该对象由new 运算符在这里创建。

    我希望 SuperClass.foo() 被调用

    正如已经解释过的,并不是没有创建对象。已经是了。并且该调用将调用被覆盖的方法。这就是为什么you should never call overridden method from constructor。你会看到意想不到的行为。

    SuperClass 对任何派生类一无所知。

    好吧,它不需要知道。方法调用调用派生类方法这一事实与超类是否知道子类无关。实际调用的方法是在运行时根据图片中的实际对象决定的。由于这里的对象是DerivedClass 类型,如果存在DerivedClass 中的方法将被调用。

    在我的情况下如何调用 SuperClass.foo()?

    你没有。这就是重点。浏览我链接的帖子以进行分步说明。

    【讨论】:

    • 感谢您的逐步解释。我知道 DerivedClass 已经存在(但尚未初始化)让我困扰的是在它不可用时访问它的可能性。
    • @TomasLongo 这就是问题所在。 Java 不会阻止你做这些事情。可能是因为发现这些东西,并将其标记为一些错误对于编译器来说是不可行的。有些事情你必须处理。
    【解决方案2】:

    与 C++ 不同,Java 在分配时设置对象的运行时类型,然后再运行任何构造函数。这就是为什么在初始化期间会出现多态行为。

    在受控情况下,这种行为有时会很有用。然而,在大多数情况下,最好避免它,因为您将未初始化的对象泄漏到类外部的代码中(通过虚拟方法中的this 引用)。您应该尽可能只从构造函数中调用私有(或最终)方法。

    【讨论】:

    • +1 永远不要让 this 引用从构造函数中泄漏出来 :)
    【解决方案3】:

    Why is DerivedClass.foo() called?

    因为这是Java语言设计。开发人员自己而不是语言来确保继承层次结构内的不变类。

    How would I call SuperClass.foo() in my case?

    来自派生类:super.foo();

    【讨论】:

      【解决方案4】:

      DerivedClass.foo() 在 DerivedClass 中被调用。为对象本身调用 foo()。通过继承,这也可以等于 SuperClass.foo()。但是,由于多态性也可以不同。如果你明确地想要 SuperClass 中的 foo(),那么调用 super.foo()。顺便说一句,因为 DerivedClass.foo() 依赖于 i,所以你应该先初始化 i。

      【讨论】:

        【解决方案5】:

        正如 Rohit 所说,不应该从构造函数中调用被覆盖的方法(或更准确地说,是可以被覆盖的方法)。这是一种解决方法:

        public class SuperClass{
            SuperClass() {
                privateFoo();
            }
        
            public void foo() {
                privateFoo();
            }
        
            private void privateFoo() {   // cannot be overridden since it's private
                System.out.println("Super.foo()");
            }
        
        }
        

        使foo 方法只是一个调用私有版本的单行方法。当然,如果它有参数和/或返回值,你会包含这些。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-03-01
          • 2011-03-09
          • 2012-03-26
          • 2014-08-06
          • 2012-10-30
          • 2017-12-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多