web-dev-qa-db-fra.com

Pourquoi Java n'a-t-il pas de constructeur de copie?

Pourquoi Java ne supporte-t-il pas un constructeur de copie comme en C++?

63
Cuga

Java fait. Ils ne sont tout simplement pas appelés implicitement comme en C++ et je suppose que c'est votre vraie question.

Tout d'abord, un constructeur de copie n'est rien d'autre que:

public class Blah {
  private int foo;

  public Blah() { } // public no-args constructor
  public Blah(Blah b) { foo = b.foo; }  // copy constructor
}

Maintenant, C++ appellera implicitement le constructeur de copie avec une instruction comme celle-ci:

Blah b2 = b1;

Le clonage/la copie dans cette instance n'a tout simplement aucun sens en Java, car tous les b1 et b2 sont des références et non des objets de valeur comme ils le sont en C++. En C++, cette instruction crée une copie de l'état de l'objet. En Java, il copie simplement la référence référence . L'état de l'objet n'est pas copié; appeler implicitement le constructeur de la copie n'a donc aucun sens.

Et c'est tout ce qu'il y a à faire vraiment.

128
cletus

De Bruce Eckel :

Pourquoi [un constructeur de copie] fonctionne-t-il en C++ et non en Java?

Le constructeur de copie est un fondamental une partie de C++, car il automatiquement fait une copie locale d'un objet. Encore l'exemple ci-dessus prouve que c'est le cas ne fonctionne pas pour Java. Pourquoi? En Java tout ce que nous manipulons est un gérer, alors que dans C++, vous pouvez avoir entités de type poignée et vous pouvez aussi passer les objets directement . C’est ce que le constructeur de copie C++ est pour: quand vous voulez prendre un objet et le transmettre par valeur, donc dupliquer l'objet. Donc ça marche bien en C++, mais vous devriez rester dans l'esprit que ce schéma échoue en Java, alors ne l'utilisez pas.

(Je recommande de lire toute la page - en fait, démarrez ici à la place.)

13
Dan Breslau

Je pense que la réponse à cette question est très intéressante.

D'une part, je crois qu'en Java, tous les objets se trouvent sur le tas, et bien que vous n'ayez pas de pointeur, vous avez des "références". Les références ont des symantiques de copie et Java en interne garde la trace des compteurs de références afin que son ramasse-miettes sache ce qu'il convient de faire disparaître.

Etant donné que vous accédez uniquement aux objets via des références pouvant être copiées, le nombre réel de copies d'un objet est considérablement réduit (par exemple, en C++, le simple fait de passer un objet à une fonction (par valeur) entraîne la construction de nouveaux objets, en Java seule la référence à l'objet est passée). Les concepteurs ont probablement pensé que clone () serait suffisant pour les utilisations restantes.

9
dicroce

C'est juste mon opinion (je suis sûr qu'il y a une réponse justifiable)

Les constructeurs de copie en C++ sont principalement utiles lorsque vous envoyez ou renvoyez des instances de classes par valeur, car c'est à ce moment-là que le constructeur de copie est activé de manière transparente.

Comme en Java, tout est renvoyé par référence et que la VM est orientée vers l'allocation dynamique, rien ne justifiait vraiment la complexité d'un constructeur de copie. 

De plus, puisque tout est par référence, un développeur doit souvent fournir sa propre implémentation et sa propre décision sur la manière de cloner des champs.

2
Uri

Je suppose qu'ils ont pensé que vous pouviez simplement créer une méthode clone ()

1
Ryan

C'est genre de fait. Lorsque des copies superficielles vous conviennent, vous avez [clone ()] ( http://Java.Sun.com/j2se/1.4.2/docs/api/Java/lang/Object.html#clone ()) et quand ils ne le sont pas, vous devez implémenter une copie complète, tout comme le C++.

La seule différence de fond est qu'il s'agit d'une méthode d'usine plutôt que d'un constructeur proprement dit, mais en termes de flexibilité et de testabilité, c'est probablement une bonne chose.

0
Aaron Maenpaa

Je ne suis pas un grand programmeur C++, mais je semble me souvenir d'une règle concernant les "trois amis" - constructeur de copie, opérateur d'affectation et destructeur. Si vous en avez un, alors vous aurez probablement besoin des trois.

Alors peut-être que sans un destructeur dans le langage, ils ne voulaient pas inclure un constructeur de copie? Juste une supposition.

0
David Hodgson

Eh bien, ça peut. Il n'est simplement pas créé implicitement. Si je devais deviner, c'est probablement dû au fait que les objets Java sont toujours alloués par tas.

En C++, le constructeur de copie par défaut est une copie superficielle au niveau des membres. Si une classe possède la mémoire allouée sur le tas (via un pointeur brut), la copie partagera les éléments internes avec l'original, ce qui n'est pas ce que vous voulez.

Imaginons un instant que Java ait ce comportement. Toute classe dont les champs sont des objets (lire: presque tous) aurait le mauvais comportement et vous devrez le remplacer vous-même. Dans 99% des cas, vous n'avez épargné aucun problème à qui que ce soit. De plus, vous venez de créer un piège subtil pour vous-même - imaginez que vous oubliez accidentellement de remplacer le constructeur de copie par défaut. S'il a été généré par défaut et que vous essayez de l'utiliser, le compilateur ne se plaindra pas du tout, mais votre programme se comportera mal au moment de l'exécution.

Même s'ils ont créé un constructeur de copie par défaut qui effectue une copie en profondeur, je ne suis pas sûr que cela serait particulièrement utile. De toute façon, non seulement vous avez tendance à effectuer moins de copies en Java que le C++, mais vous ne voulez pas toujours copier en profondeur un champ.

Les objets que vous possédez et les objets auxquels vous faites référence parce que vous en avez besoin, mais que vous n’êtes pas responsable, sont les mêmes, à savoir des champs. La propriété et l'emprunt ne sont pas des concepts de première classe. Pour les objets que vous possédez, vous souhaitez les copier en profondeur (à moins qu'ils ne soient immuables, dans ce cas, vous ne devriez pas vous embêter), et pour les objets pour lesquels vous ne détenez qu'une référence, vous souhaitez copier la référence.

Je dirais qu'un constructeur de copie qui copie tout simplement sans mémoire ne conviendrait pas non plus pour beaucoup de classes. Certainement plus que la copie superficielle par défaut, cependant.

0
Kevin