Почему в Java нет списков инициализаторов, как на С++?

В С++ вы можете использовать список инициализаторов для инициализации полей класса до запуска конструктора. Например:

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

Мне любопытно, почему Java не имеет подобной функции. Согласно Core Java: Том 1:

С++ использует этот специальный синтаксис для вызова конструкторов полей. В Java нет необходимости в нем, поскольку объекты не имеют подобъектов, только указатели на другие объекты.

Вот мои вопросы:

  • Что они понимают под "потому что объекты не имеют подобъектов?" Я не понимаю, что такое подобъект (я попытался найти его); они означают создание подкласса, который расширяет суперкласс?

  • Что касается того, почему в Java нет списков инициализаторов, таких как С++, я бы предположил, что причина в том, что все поля уже инициализируются по умолчанию в Java, а также потому, что Java использует ключевое слово super для вызова супер ( или база в С++ lingo) -класс. Правильно ли это?

Ответ 1

В С++ списки инициализаторов необходимы из-за нескольких языковых функций, которые либо не присутствуют в Java, либо работают по-разному в Java:

  • const. В С++ вы можете определить поля, отмеченные const, которые нельзя назначить и должны быть инициализированы в списке инициализаторов. В Java есть поля final, но вы можете назначить поля final в теле конструктора. В С++ назначение в const поля в конструкторе является незаконным.

  • Ссылки. В С++ ссылки (в отличие от указателей) должны быть инициализированы для привязки к некоторому объекту. Невозможно создать ссылку без инициализатора. В С++ способ, которым вы указываете это, - это список инициализаторов, так как если бы вы ссылались на ссылку в теле конструктора без первой инициализации, вы использовали бы неинициализированную ссылку. В Java ссылки на объекты ведут себя как указатели С++ и могут быть назначены после создания. Они просто по умолчанию null в противном случае.

  • Прямые подобъекты. В С++ объект может содержать объект непосредственно как поля, тогда как в объектах Java могут храниться только ссылки на эти объекты. То есть, в С++, если вы объявляете объект с string в качестве члена, пространство для хранения этой строки строится непосредственно в пространстве для самого объекта, тогда как в Java вы просто получаете место для ссылки на некоторые другой объект string, сохраненный в другом месте. Следовательно, С++ должен предоставить вам способ дать исходные значения этих субобъектов, поскольку в противном случае они просто оставались бы неинициализированными. По умолчанию он использует конструктор по умолчанию для этих типов, но если вы хотите использовать другой конструктор или нет конструктора по умолчанию, список инициализаторов дает вам возможность обойти это. В Java вам не нужно беспокоиться об этом, потому что ссылки будут по умолчанию равны null, и вы можете затем назначить их для ссылки на объекты, которые вы на самом деле хотите, чтобы они ссылались. Если вы хотите использовать конструктор, отличный от стандартного, то для него вам не нужен специальный синтаксис; просто установите ссылку на новый объект, инициализированный с помощью соответствующего конструктора.

В тех немногих случаях, когда Java может потребовать списки инициализаторов (например, для вызова конструкторов суперклассов или предоставления значений по умолчанию для своих полей), это обрабатывается двумя другими языковыми функциями: ключевое слово super для вызова конструкторов суперклассов и тот факт, что объекты Java могут давать свои значения по умолчанию для полей в момент их объявления. Поскольку С++ имеет множественное наследование, просто наличие одного ключевого слова super недвусмысленно относится к одному базовому классу, а до С++ 11 С++ не поддерживал инициализаторы по умолчанию в классе и должен был полагаться на списки инициализаторов.

Надеюсь, это поможет!

Ответ 2

С++

Существует разница между

ClassType t(initialization arguments);

и

ClassType * pt;

Последнее не нужно инициализировать (установлено в NULL). Первый делает. Подумайте об этом как о целочисленном. Вы не можете иметь int без значения, НО, у вас может быть указатель int без значения.

Итак, когда у вас есть:

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

Тогда объявление:

ClassType object;

автоматически создает экземпляр OtherClass в value. Поэтому, если OtherClass имеет инициализацию, это должно быть сделано в конструкторе ClassType. Однако reference - это просто указатель (адрес в памяти) и может оставаться неинициализированным. Если вам нужен экземпляр OtherClass, вы должны использовать

object.reference = new OtherClass(initialization arguments);

Java

Только

class ClassType
{
    OtherClass reference;
}

Это эквивалентно указателю на С++. В этом случае, когда вы выполните:

ClassType object = new ClassType();

Вы автоматически не создаете экземпляр OtherClass. Поэтому вам не нужно инициализировать что-либо в конструкторе, если вы этого не хотите. Если вы хотите объект OtherClass, вы можете использовать

object.reference = new OtherClass();

Ответ 3

Поскольку Java не нуждается в них, чтобы разрешить инициализацию полей, тип которых не имеет нулевого значения.

В С++

class C {
  D d;
}

без инициализатора элемента для d, D::D() будет вызываться, что делает невозможным инициализацию поля, если для d нет нулевого типа. Это может произойти, когда D::D() явно объявлено private.

В Java существует известное нулевое значение для всех ссылочных типов null, поэтому поле всегда можно инициализировать.

Java также выполняет кучу работы, чтобы убедиться, что * все поля final инициализированы перед первым использованием и до завершения конструктора, поэтому, когда у Java есть требование, такое как требование инициализации поля С++ const, оно просто перегружает this.fieldName = <expression> в корпусе конструктора для обозначения инициализации поля.

  • : исключения по модулю, вызванные в ctor, переопределенные вызовы метода из базового класса и т.д.