【问题标题】:Why doesn't Java have initializer lists like in C++?为什么 Java 没有像 C++ 中那样的初始化列表?
【发布时间】:2011-11-01 13:12:23
【问题描述】:

在 C++ 中,您可以使用初始化列表在构造函数开始运行之前初始化类的字段。例如:

Foo::Foo(string s, double d, int n) : name(s), weight(d), age(n) {
    // Empty; already handled!
}

我很好奇为什么 Java 没有类似的功能。根据Core Java:第 1 卷:

C++ 使用这种特殊的语法来调用字段构造函数。在 Java 中,不需要它,因为对象没有子对象,只有指向其他对象的指针。

这是我的问题:

  1. “因为对象没有子对象”是什么意思?我不明白什么是子对象(我试着查了一下);它们是指扩展超类的子类的实例化吗?

  2. 至于为什么Java没有像C++这样的初始化列表,我认为是因为Java中所有字段都已经默认初始化了,也因为Java使用super关键字来调用super(或 C++ 术语中的基础)-类构造函数。这是正确的吗?

【问题讨论】:

    标签: java c++ constructor initialization initialization-list


    【解决方案1】:

    在 C++ 中,初始化列表是必要的,因为一些语言特性在 Java 中不存在或在 Java 中的工作方式不同:

    1. const:在 C++ 中,您可以定义一个标记为 const 的字段,该字段不能分配给并且必须在初始化列表中初始化。 Java 确实有final 字段,但您可以在构造函数的主体中分配给final 字段。在 C++ 中,在构造函数中分配给 const 字段是非法的。

    2. 引用:在 C++ 中,引用(相对于指针)必须初始化才能绑定到某个对象。在没有初始化程序的情况下创建引用是非法的。在 C++ 中,您指定它的方式是使用初始化列表,因为如果您要在构造函数主体中引用引用而不首先对其进行初始化,那么您将使用未初始化的引用。在 Java 中,对象引用的行为类似于 C++ 指针,可以在创建后分配。他们只是默认为null,否则。

    3. 直接子对象。在 C++ 中,对象可以直接包含对象作为字段,而在 Java 中,对象只能保存对这些对象的 引用。也就是说,在 C++ 中,如果您声明一个具有 string 作为成员的对象,则该字符串的存储空间直接构建到对象本身的空间中,而在 Java 中,您只需获得空间来引用一些其他 String 对象存储在别处。因此,C++ 需要为您提供一种方法来赋予这些子对象初始值,否则它们将保持未初始化状态。默认情况下,它为这些类型使用默认构造函数,但如果您想使用不同的构造函数或没有可用的默认构造函数,初始化列表为您提供了一种绕过它的方法。在 Java 中,您不必担心这一点,因为引用将默认为 null,然后您可以将它们分配给您实际希望它们引用的对象。如果要使用非默认构造函数,则不需要任何特殊语法;只需将引用设置为通过适当的构造函数初始化的新对象。

    在 Java 可能需要初始化列表的少数情况下(例如,调用超类构造函数或为其字段提供默认值),这是通过其他两个语言特性来处理的:super 关键字调用超类构造函数,以及Java 对象可以在声明它们的时候为其字段赋予默认值这一事实。由于 C++ 具有多重继承,因此仅具有单个 super 关键字不会明确引用单个基类,并且在 C++11 之前,C++ 不支持类中的默认初始化程序并且必须依赖初始化程序列表。

    希望这会有所帮助!

    【讨论】:

    • 优秀的答案。我对我的问题的响应时间感到非常惊讶。注册后我会为你投票。此外,我们将不胜感激提供有关 C++0x 更改的链接。
    • @Jesse- 很高兴为您提供帮助!如果您认为它回答了问题,也不要忘记接受答案。 :-) 我在问题中包含了 C++0x 更改的链接。
    • @templatetypedef:In C++, references (as opposed to pointers) must be initialized to bind to some object.。我认为,它不是“必须初始化”。您可以在有效程序中有未初始化的引用;只是你不能使用它,以避免未定义的行为。 :-)。为好帖子 +1。
    【解决方案2】:

    C++

    有区别

    ClassType t(initialization arguments);
    

    和

    ClassType * pt;
    

    后者不需要初始化(设置为NULL)。前者可以。把它想象成一个整数。你不能有一个没有值的 int,但是你可以有一个没有值的 int 指针。

    所以当你有:

    class ClassType
    {
        OtherClass value;
        OtherClass * reference;
    };
    

    然后声明:

    ClassType object;
    

    在value 中自动创建OtherClass 的实例。因此,如果OtherClass 有初始化,则必须在ClassType 构造函数中完成。但是,reference 只是一个指针(内存中的地址),可以保持未初始化状态。如果你想要OtherClass 的实例,你必须使用

    object.reference = new OtherClass(initialization arguments);
    

    Java

    只有一个

    class ClassType
    {
        OtherClass reference;
    }
    

    这相当于 C++ 中的指针。在这种情况下,当你这样做时:

    ClassType object = new ClassType();
    

    您不会自动创建OtherClass 的实例。因此,除非您愿意,否则您不必在构造函数中初始化任何内容。当您想要OtherClass 的对象时,您可以使用

    object.reference = new OtherClass();
    

    【讨论】:

      【解决方案3】:

      因为 Java 不需要它们来允许初始化类型不为零值的字段。

      在 C++ 中

      class C {
        D d;
      }
      

      如果没有d 的成员初始化程序,D::D() 将被调用,如果D 没有零类型,则无法初始化该字段。当 D::D() 被显式声明为 private 时,可能会发生这种情况。

      在 Java 中,所有引用类型都有一个已知的 zero-valuenull,因此始终可以初始化字段。

      Java 还做了很多工作来确保*所有 final 字段在首次使用之前和构造函数结束之前都被初始化,所以虽然 Java 有像 C++ 的 const 字段初始化要求这样的要求,但它只是重载了 @构造函数体中的 987654334@ 表示字段初始化。

      • :在 ctor 中抛出模异常、基类中的重写方法调用等。

      【讨论】:

      • 感谢您的回答。但是,我有点厌倦“零值”这个术语,因为它听起来像是在谈论整数 0,而在 C++Ox (nullptr) 和 Java 中我不认为 null 是等价的。
      • @Jesse,我没有发明“零值”这个词。它是编程语言规范文档等中的常用术语。例如。 "Effective Go" 谈到返回参数时说,“当命名时,它们在函数开始时被初始化为其类型的零值”,language spec 将 nil 定义为指针类型的零值。
      • 感谢有关“零值”的链接。您可能知道,不同的语言对类似概念使用不同的术语,例如here。所以,我建议区分两者。
      • @Jesse,是的。我认为说明符期望 bzeroing 对象内存将其所有字段设置为 zero-value 的语言,如 Go 中一样,倾向于使用该术语,而 Java 语言规范使用“默认值”,因为 Java 没有指定一个名为“不安全”的模块,需要指定有关对象布局的任何内容。也许“默认值”或“初始值”会更好,特别是因为“零值”w.r.t. IEEE 754 值提示问题“哪个零?”
      猜你喜欢
      • 2014-06-09
      • 2020-04-18
      • 2019-06-28
      • 2011-02-06
      • 1970-01-01
      • 2010-09-24
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      相关资源
      最近更新 更多