为什么子类构造函数必须显式调用父类构造函数?

6
4个回答

18

他们不会。

如果您没有显式调用超级构造函数,那么它等同于调用无参数的超级构造函数。

public class Sub
{
    public Sub()
    {
        // Do some stuff
    }
}

相当于:

public class Sub
{
    public Sub()
    {
        super();
        // Do some stuff
    }
}

如果你想指定参数,那么你确实需要显式调用超类构造函数。在我看来,这是相当合理的 - 你难道真的希望编译器去“猜测”你想要提供哪些参数吗?


1
另外,如果您的父类没有无参数构造函数,则需要使用必要的参数显式调用super构造函数。 - Claudio
4
是的,我希望这是暗示了的 - 如果没有无参构造函数,就不能隐式调用它 :) - Jon Skeet
3
没有。当没有其他构造函数被指定时,编译器会提供一个默认构造函数。如果您指定了带参数的构造函数但没有无参构造函数,那么在子类尝试隐式调用无参构造函数时将失败。 - Jon Skeet
@Pere:在这种情况下,在超类中引入一个新的构造函数会改变代码的含义,这是一个问题——除非你也说它不应该在没有匹配的情况下隐式调用super()... - Jon Skeet
抱歉@JonSkeet,但我不明白你在说什么,也不认为这是我所质疑的答案。假设我在我的基类中有一个构造函数,它接受一个String参数。如果我扩展我的类,我必须在我的子类中显式声明一个构造函数,该构造函数需要一个String,并且如果我不想实现进一步的逻辑,只需在其上调用super(myString);。为什么?为什么编译器不能自动推断出这个呢? - Pere
显示剩余11条评论

3
如上所述,如果父类中没有默认构造函数,则只需调用超级构造函数即可。
这是必须的,因为父类必须由其构造函数之一初始化,如果没有默认构造函数,Java编译器就无法知道应该调用哪个构造函数或需要传递什么参数。
为了更好地理解为什么必须调用父类中至少一个构造函数,请考虑以下内容:
class Person {
    private Person mother;
    private Person father;

    public Person(Person mother, Person father) {
        assert mother != null && father != null: "Parents can't be null!";

        this.mother = mother;
        this.father = father;
    }

    public boolean hasAnyLivingParents() {
        return mother.isAlive() || father.isAlive();
    }

    public boolean isAlive() { return true; }
}

如果您直接创建一个Person,则必须指定该人的motherfather,而hasAnyLivingParents()方法需要指定这些信息。
现在,考虑您有一个子类Employee,并且您不关心雇员的父母,因此您想编写如下代码:
class Employee extends Person {
    double salary;

    public Employee(double salary) { 
        this.salary = salary;
    }
}

这段代码无法编译,因为我们没有调用 Person 的构造函数,并且也没有默认构造函数。即使这段代码能够编译,调用 (new Employee(50000d)).hasAnyLivingParents() 也会 总是 抛出 NullPointerException,因为 motherfather 字段没有被初始化。

简言之,Java 要求 每个类都必须由某个构造函数进行初始化。如果一个类没有默认构造函数,则必须调用它的其他构造函数来初始化对象。


2

当子类调用父类中存在的非参数化默认构造函数时,它会隐式地调用该函数。但是当我们向构造函数传递参数时,我们必须显式地调用它。


@Pere 正如 javadoc 中所述,您不必为您的类提供任何构造函数,但在这样做时必须小心。编译器会自动为没有构造函数的任何类提供一个无参默认构造函数。此默认构造函数将调用超类的无参构造函数。在这种情况下,如果超类没有无参构造函数,则编译器会发出警告,因此您必须验证它是否存在。如果您的类没有显式超类,则它具有 Object 的隐式超类,该超类具有无参构造函数。 - Abhishekkumar
谢谢你的回答,Abhishekkumar。我知道这是一个老问题,但我问这个问题是因为你没有回答原始问题。我们知道当我们不传递参数到构造函数时必须显式调用。但他所问的是为什么。我们已经知道该怎么做了。另外,你引用的javadoc摘录与问题几乎没有关系。我们必须小心在超类中有一个非参数构造函数;好的。但问题是另一个。为什么我必须执行super(),它只是将子构造函数的参数传递给父级,仅此而已? - Pere
调用super()是必需的,因为父类必须由其中一个构造函数进行初始化,如果没有默认构造函数,Java编译器就无法知道要调用哪个构造函数。因此,通过内部或显式调用,我们通过其构造函数初始化类。 - Abhishekkumar
然后编译器可能会抱怨父类没有构造函数。对我来说,这根本不是一个答案。 - Pere
你甚至让我感到困惑了。实际上,我不需要知道那些细节(而且,顺便问一下,你确定它真的是那样做的吗?)。我重申:我不想要关于如何完成它的解释。我只是在问为什么不能用其他语言那样,通过查看构造函数的签名来推断应该调用哪个父构造函数,而不是手动执行它(如果我能手动执行它,编译器也可以)。就这样。 - Pere
显示剩余2条评论

1
class Parent
{ 
   Parent(int x) {}
}

class Child extends Parent
{
   Child(){} // will not compile.
}

编译器试图在Child()构造函数的第一行调用super(),但父类没有无参构造函数。因此,在这种情况下,您必须通过显式调用super(5)来执行它。

你可能忘记在Child类中添加 extends。 - Alpit Anand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接