web-dev-qa-db-fra.com

Pourquoi Java n'a-t-il pas de listes d'initialisation comme en C ++?

En C++, vous pouvez utiliser une liste d'initialisation pour initialiser les champs de la classe avant que le constructeur ne commence à s'exécuter. Par exemple:

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

Je suis curieux de savoir pourquoi Java n'a pas de fonctionnalité similaire. Selon Core Java: Volume 1:

C++ utilise cette syntaxe spéciale pour appeler les constructeurs de champs. En Java, il n'est pas nécessaire car les objets n'ont pas de sous-objets, seulement des pointeurs vers d'autres objets.

Voici mes questions:

  1. Qu'entendent-ils par "parce que les objets n'ont pas de sous-objets?" Je ne comprends pas ce qu'est un sous-objet (j'ai essayé de le rechercher); signifient-ils une instanciation d'une sous-classe qui prolonge une super-classe?

  2. Quant à savoir pourquoi Java n'a pas de listes d'initialisation comme C++, je suppose que la raison en est que tous les champs sont déjà initialisés par défaut dans Java et aussi parce que Java utilise le mot clé super pour appeler le constructeur de classe super (ou base dans le jargon C++). Est-ce correct?

54
Jesse Good

En C++, les listes d'initialisation sont nécessaires en raison de quelques fonctionnalités de langage qui ne sont pas présentes dans Java ou fonctionnent différemment en Java:

  1. const: En C++, vous pouvez définir des champs marqués const qui ne peuvent pas être affectés et doivent être initialisés dans la liste d'initialisation. Java possède des champs final, mais vous pouvez les affecter à final champs dans le corps d'un constructeur. En C++, affecter à un const dans le constructeur est illégal.

  2. Références: En C++, les références (par opposition aux pointeurs) doivent être initialisées pour se lier à un objet. Il est illégal de créer une référence sans initialiseur. En C++, la façon dont vous spécifiez cela est avec la liste d'initialisation, car si vous deviez faire référence à la référence dans le corps du constructeur sans l'initialiser au préalable, vous utiliseriez une référence non initialisée. En Java, les références d'objet se comportent comme des pointeurs C++ et peuvent être affectées à une fois créées. Ils sont simplement par défaut à null sinon.

  3. Sous-objets directs. En C++, un objet peut contenir un objet directement sous forme de champs, alors qu'en Java les objets ne peuvent contenir que les références à ces objets C'est-à-dire qu'en C++, si vous déclarez un objet qui a un string en tant que membre, l'espace de stockage de cette chaîne est construit directement dans l'espace de l'objet lui-même, tandis qu'en Java vous obtenez juste de l'espace pour une référence à un autre String objet stocké ailleurs. Par conséquent, C++ doit vous fournir un moyen de donner à ces sous-objets les valeurs initiales, sinon ils resteraient simplement non initialisé. Par défaut, il utilise le constructeur par défaut pour ces types, mais si vous souhaitez utiliser un constructeur différent ou qu'aucun constructeur par défaut n'est disponible, la liste d'initialisation vous donne un moyen de contourner cela. En Java, vous n'avez pas à vous soucier de cela parce que les références seront par défaut à null, et vous pouvez ensuite les affecter pour faire référence aux objets auxquels vous voulez qu'ils se réfèrent. Si vous souhaitez utiliser un constructeur non par défaut, alors vous d on n'a pas besoin de syntaxe spéciale pour cela; il suffit de définir la référence à un nouvel objet initialisé via le constructeur approprié.

Dans les quelques cas où Java peut vouloir des listes d'initialisation (par exemple, pour appeler des constructeurs de superclasse ou donner des valeurs par défaut à ses champs), cela est géré par le biais de deux autres fonctionnalités de langage: le super mot-clé pour appeler les constructeurs de superclasses, et le fait que Java peuvent donner à leurs champs des valeurs par défaut au point où ils sont déclarés. Puisque C++ a un héritage multiple, il n'en reste qu'un super mot-clé ne ferait pas référence sans ambiguïté à une seule classe de base, et avant C++ 11, C++ ne supportait pas les initialiseurs par défaut dans une classe et devait s'appuyer sur des listes d'initialiseurs.

J'espère que cela t'aides!

97
templatetypedef

C++

Il y a une différence entre

ClassType t(initialization arguments);

et

ClassType * pt;

Ce dernier n'a pas besoin d'être initialisé (défini sur NULL). Le premier le fait. Considérez-le comme un entier. Vous ne pouvez pas avoir un int sans valeur, MAIS vous pouvez avoir un pointeur int sans valeur.

Donc, quand vous avez:

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

Puis la déclaration:

ClassType object;

crée automatiquement une instance de OtherClass dans value. Par conséquent, si OtherClass a une initialisation, elle doit être effectuée dans le constructeur ClassType. Cependant, reference n'est qu'un pointeur (adresse en mémoire) et peut rester non initialisé. Si vous voulez une instance de OtherClass vous devez utiliser

object.reference = new OtherClass(initialization arguments);

Java

Il n'y a que

class ClassType
{
    OtherClass reference;
}

Cela équivaut à un pointeur en C++. Dans ce cas, lorsque vous le faites:

ClassType object = new ClassType();

Vous ne créez pas automatiquement une instance de OtherClass. Par conséquent, vous n'avez rien à initialiser dans le constructeur, sauf si vous le souhaitez. Lorsque vous voulez un objet de OtherClass vous pouvez utiliser

object.reference = new OtherClass();
9
George

Parce que Java n'en a pas besoin pour permettre l'initialisation des champs dont le type n'a pas de valeur nulle.

En C++

class C {
  D d;
}

sans initialiseur de membre pour d, D::D() sera appelée, ce qui rend impossible l'initialisation du champ s'il n'y a pas de type zéro pour D. Cela peut se produire lorsque D::D() est explicitement déclaré private.

En Java, il existe un valeur nulle connu pour tous les types de référence, null, donc un champ peut toujours être initialisé.

Java fait également beaucoup de travail pour s'assurer * que tous les champs final sont initialisés avant la première utilisation et avant la fin du constructeur, donc tandis que Java a une exigence comme C++ _ const exigence d'initialisation du champ, il surcharge simplement this.fieldName = <expression> dans le corps du constructeur pour signifier l'initialisation du champ.

  • : exceptions modulo levées dans le ctor, appels de méthode surchargés depuis la classe de base, etc.
1
Mike Samuel