【问题标题】:When to call Java super constructors?何时调用 Java 超级构造函数?
【发布时间】:2021-06-29 21:07:09
【问题描述】:

好的,所以我正在阅读这本 Java 书籍“Java 21 天”,作者提出了一些我认为是我读过的任何编程书籍中最令人困惑的主张。我认为作者只是在表达观点方面做得很差。这是最令人困惑的两点,如果有经验的 Java 开发人员能更清楚地解释它们,我将不胜感激。

第 1 点:

构造函数不能被覆盖。因为构造函数总是与当前类同名,所以新的构造函数被创建而不是被继承。这在很多时候就足够了;当您的类的构造函数被调用时,所有超类的具有相同签名的构造函数也会被调用。因此,您继承的类的所有部分都可以进行初始化。

认为我明白他在说什么。您必须在子类构造函数中使用 super() 构造函数来初始化超类的任何必要属性。也就是说,如果超类构造函数需要参数,您可以通过 super() 传递这些参数。但是当你说“当你的类的构造函数被调用时,对你的所有超类具有相同签名的构造函数也被调用”时,他绝对失去了我。在那之前的立即这一行,他提到了构造函数是如何不被继承的,因为它们总是与类同名;那么,他的意思是调用超类中具有相同签名的构造函数呢?没有一个超类构造函数与子类的构造函数具有相同的签名,因为它们具有不同的名称。有人可以澄清一下吗?

作者在第2点做了类似的陈述:

您不必调用超类中与您的类中的构造函数具有相同签名的构造函数;您必须仅为需要初始化的值调用构造函数。事实上,您可以创建一个类,其构造函数的签名与任何超类的构造函数完全不同。

!!!再次,有人可以在这里澄清吗?他说“创建一个类,它的构造函数与任何超类的构造函数都具有完全不同的签名”。这不是隐含的情况吗?构造函数必须具有与其所有超类不同的名称,因此也必须具有不同的签名。再次,我真的很感谢这里有经验的 Java 开发人员的解释,因为我很困惑。

作者甚至没有提供任何代码来说明这些观点。如果你能提供一些代码和你的解释,这对我来说将是非常有成效的。谢谢!如果我需要澄清或添加更多上下文,请告诉我。

【问题讨论】:

  • “所有超类的签名相同的构造函数也被调用”——这不是真的。除非您显式调用 super(...),否则将调用超类的无参数构造函数。
  • 听起来构造函数Child(int a, int b) 不需要调用,Parent(int a, int b) 因为它们具有相同的签名。如果只有Parent(int a),你可以打电话给super(a)
  • 在谈论签名时,您通常不会考虑名称。只有参数和返回类型。 (可能是例外规范或其他“装饰”。)
  • 这是一个示例,ideone.com/b4NaVS#stdin 在这种情况下需要调用 super。所以听起来作者是不正确的。
  • @matt 然后你必须明确地调用super(...)

标签: java class object oop constructor


【解决方案1】:

您正在阅读的文字具有误导性或错误。

听起来它想说的是,如果你有一个类A 和一个构造函数A(int) 和一个A 的子类B 和一个构造函数B(int),那么该构造函数将自动调用A(int) 构造函数。

但事实并非如此。您在子类中编写的任何构造函数都会隐式调用其超类的无参数构造函数,除非您显式调用super(...)

所以

class B extends A {
    B(int value) {
        // implicitly calls the no arguments constructor, A()
    } 
}

如果无参数构造函数不可用(不存在或私有),那么您必须在子类构造函数中调用super(...),提供与超类中可用构造函数匹配的参数。

来自https://docs.oracle.com/javase/tutorial/java/IandI/super.html

如果构造函数没有显式调用超类构造函数,Java 编译器会自动插入对超类的无参数构造函数的调用。

【讨论】:

  • @matt 不,不是。调用 A() 构造函数。在该代码中,它们都嵌套在一个名为Ideone 的容器类中。他们都可以看到彼此的私人成员,因为他们都在同一个班级。
  • @matt 可以被包含类Ideone内的任意代码调用。
  • @matt 不,你不能。标准输出为空。它是被调用的无参数构造函数,
【解决方案2】:

默认情况下,子类构造函数会不带参数地调用超类构造函数。这是隐含的。

class A {

}

class B extends A{
   int b;
   public B(int bParam) {
      // implicit super()  call to A
      b = bParam;
   }

}

但是假设你的超类没有任何无参数构造函数并且你想初始化一些超类变量,你必须使用参数进行显式 super() 调用。

class A {
   int a;
   public A(int aParam) {
      a = aParam;
   }

}

class B extends A{
   int b;
   public B(int aParam, int bParam) {
      //initialize a field in super class A
      super(aParam);
      b = bParam;
   }
}

所以下面两个 cmets 是不正确的。这与相同的签名构造函数无关。只会调用超类的无参数构造函数。

这在很多时候已经足够了;当你的类的构造函数是 调用,具有相同签名的构造函数为您的所有 超类也被调用。

你不必在你的超类中调用具有 与您的类中的构造函数相同的签名;你必须打电话给 构造函数仅用于您需要初始化的值

【讨论】:

  • 您错过了一个案例,您的 A 类有一个具有相同签名的构造函数。
  • 相同的签名或不同的签名,在这两种情况下,如果需要,子类构造函数都必须使用参数进行显式 super() 调用。这就是为什么没有将其作为单独的场景提及。
  • 但这就是问题所在。
  • 是的。我已经提到只会从子类调用无参数 super() 构造函数。任何其他情况,我们都需要显式调用 super()。无论如何,希望现在可以从 cmets 中澄清这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-16
  • 1970-01-01
  • 2021-11-23
  • 1970-01-01
  • 2011-06-08
  • 1970-01-01
相关资源
最近更新 更多