Pourquoi la méthode clone () est-elle protégée dans java.lang.Object?

112

Quelle est la raison spécifique clone()définie comme protégée dansjava.lang.Object ?

Alex N.
la source

Réponses:

107

Le fait que le clone soit protégé est extrêmement douteux - tout comme le fait que la cloneméthode ne soit pas déclarée dans l' Cloneableinterface.

Cela rend la méthode assez inutile pour prendre des copies de données car vous ne pouvez pas dire :

if(a instanceof Cloneable) {
    copy = ((Cloneable) a).clone();
}

Je pense que la conception de Cloneableest maintenant largement considérée comme une erreur (citation ci-dessous). Je voudrais normalement pouvoir faire des implémentations d'une interface Cloneablemais pas nécessairement faire l'interfaceCloneable (similaire à l'utilisation de Serializable). Cela ne peut se faire sans réflexion:

ISomething i = ...
if (i instanceof Cloneable) {
   //DAMN! I Need to know about ISomethingImpl! Unless...
   copy = (ISomething) i.getClass().getMethod("clone").invoke(i);
}

Citation de Effective Java de Josh Bloch :
"L'interface clonable était conçue comme une interface mixin pour que les objets annoncent qu'ils permettent le clonage. Malheureusement, cela ne sert pas à cette fin ... Il s'agit d'une utilisation très atypique des interfaces et non d'une utilisation à émuler ... Pour que l’implémentation de l’interface ait un effet sur une classe, celle-ci et toutes ses superclasses doivent obéir à un protocole assez complexe, inapplicable et largement non documenté »

oxbow_lakes
la source
2
Si l'objet n'est pas clonable, alors le clone () de Object lancera CloneNotSupportedException. Donc, vous devez être clonable si vous allez appeler super.clone () (ce qui entraîne l'appel d'Object.clone ()). Je ne vois pas comment un objet peut être sérialisé sans implémenter Serializable.
Steve Kuo le
1
"Je pense que la conception de Cloneable est maintenant largement considérée comme une erreur." [la citation nécessaire]
Kevin Panko
Désolé - je n'impliquais pas cela. J'impliquais simplement qu'une «bonne» conception ne consiste pas à étendre une interface Serializable- c'est aux implémentations de décider de l'implémenter Serializable. J'étendais cela à Cloneable- ce n'est pas quelque chose qu'une interface devrait étendre - mais une implémentation d'une interface est libre de l'être Cloneable. Le problème est que si vous avez un paramètre du type interface, vous lui demandez s'il est clonable; mais alors vous ne pouvez pas réellement le cloner!
oxbow_lakes
6
@Kevin - Java pp45 efficace de Josh Bloch . "L'interface clonable était conçue comme une interface mixin pour que les objets annoncent qu'ils autorisent le clonage. Malheureusement, il ne parvient pas à remplir cet objectif"
oxbow_lakes
Toujours sur la même page: "Il s'agit d'une utilisation très atypique des interfaces et non à émuler" et "Pour que l'implémentation de l'interface ait un effet sur une classe, elle et toutes ses superclasses doivent obéir à un fonctionnement assez complexe, protocole inapplicable et largement non documenté "
oxbow_lakes
30

L'interface clonable n'est qu'un marqueur indiquant que la classe peut prendre en charge le clonage. La méthode est protégée car vous ne devez pas l'appeler sur un objet, vous pouvez (et devriez) la remplacer comme publique.

Du soleil:

Dans la classe Object, la méthode clone () est déclarée protégée. Si tout ce que vous faites est d'implémenter Cloneable, seuls les sous-classes et les membres du même package pourront invoquer clone () sur l'objet. Pour permettre à n'importe quelle classe de n'importe quel package d'accéder à la méthode clone (), vous devrez la remplacer et la déclarer publique, comme ci-dessous. (Lorsque vous substituez une méthode, vous pouvez la rendre moins privée, mais pas plus privée. Ici, la méthode protected clone () dans Object est remplacée en tant que méthode publique.)

Bill K
la source
Ce qui est bien, jusqu'à ce que vous apportiez des interfaces dans le mélange - essayez de cloner une implémentation inconnue deSet
oxbow_lakes
@oxbow_lakes: mais peut-être que certaines implémentations de Set ne sont pas clonables
newacct
3
Vous ne pouvez pas cloner quoi que ce soit qui n'implémente pas l'interface Clonable - c'est un marqueur qui dit "Cette classe est correctement clonable" - très similaire à l'interface Serializable. Au fait, il existe un moyen de cloner des classes via la sérialisation qui fonctionne bien - google quelque chose comme "java serialization clone" et vous trouverez probablement une poignée de moyens pour obtenir une copie complète de votre objet.
Bill K
4
Vous ne pouvez pas cloner quoi que ce soit qui n'implémente pas l'interface clonable, mais ce n'est pas parce que quelque chose implémente l'interface clonable que vous pouvez la cloner.
Michael Myers
1
@BuckCherry toString a une implémentation par défaut, si vous l'appelez, quelque chose de bien se produira et vous récupérerez une chaîne. equals a une implémentation par défaut (identique à ==). Le clone ne peut pas avoir d'implémentation par défaut. Si vous appelez clone sur un objet qui ne l'a pas implémenté, vous n'obtiendrez pas un comportement raisonnable. Le clonage est compliqué et ne peut pas vraiment être fait automatiquement (certains objets sans constructeur par défaut peuvent être impossibles à cloner de manière générique), donc par défaut, ils le rendent un peu plus sûr. Je pense que le mettre sur Object du tout, cependant, n'aurait peut-être pas été nécessaire.
Bill K
7

cloneest protégé car c'est quelque chose qui doit être remplacé pour qu'il soit spécifique à la classe actuelle. Bien qu'il soit possible de créer une cloneméthode publique qui clonerait n'importe quel objet, cela ne serait pas aussi bon qu'une méthode écrite spécifiquement pour la classe qui en a besoin.

Andrew Hare
la source
mais pourquoi doit-il être protégé pour cela?
Janusz
3
Il est protégé afin que vous n'utilisiez pas celui de l'objet (il lancera de toute façon une exception). Ils veulent que vous le remplaciez dans une classe, puis que vous le rendiez public. (répondu quelques fois ci-dessous aussi)
Bill K
1
Et inutile lorsque l'on considère les interfaces selon mon point ci
oxbow_lakes
devrait être remplacé n'est pas très clair alors il aurait dû être abstrait et ne doit pas être dans la classe d'objets, j'essaie de comprendre pourquoi il en est ainsi.
Kumar Abhishek le
4

La méthode Clone ne peut être utilisée directement sur aucun objet, c'est pourquoi elle est destinée à être remplacée par la sous-classe.

Bien sûr, cela pourrait être public et simplement lancer une exception appropriée lorsque le clonage n'est pas possible, mais je pense que ce serait trompeur.

La façon dont le clone est implémenté actuellement vous fait réfléchir à la raison pour laquelle vous souhaitez utiliser le clonage et à la manière dont vous souhaitez que votre objet soit cloné.

Silfverstrom
la source
2

Il est protégé car l'implémentation par défaut fait une copie superficielle de tous les champs (y compris privé), en contournant le constructeur . Ce n'est pas quelque chose qu'un objet peut être conçu pour gérer en premier lieu (par exemple, il peut garder une trace des instances d'objet créées dans une liste partagée, ou quelque chose de similaire).

Pour la même raison, l'implémentation par défaut de clone()lancera si l'objet sur lequel elle est appelée ne l'implémente pas Cloneable. C'est une opération potentiellement dangereuse avec des conséquences importantes, et par conséquent, l'auteur de la classe doit explicitement s'y inscrire.

Pavel Minaev
la source
En fait, l'implémentation par défaut (dans l'objet) lève une exception selon la documentation ...
Bill K
2
Non, ça ne fait pas que lancer. De son JavaDoc: "La méthode clone pour la classe Object effectue une opération de clonage spécifique. Premièrement, si la classe de cet objet n'implémente pas l'interface Cloneable, une CloneNotSupportedException est lancée. Notez que tous les tableaux sont considérés comme implémentant l'interface Cloneable. Sinon, cette méthode crée une nouvelle instance de la classe de cet objet et initialise tous ses champs avec exactement le contenu des champs correspondants de cet objet, comme par affectation; le contenu des champs n'est pas lui-même cloné. "
Pavel Minaev
2

Du javadoc de cloneable.

* By convention, classes that implement this interface (cloneable) should override 
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.

* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface.  Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.

Vous pouvez donc appeler clone sur chaque objet, mais cela ne vous donnera la plupart du temps pas les résultats que vous souhaitez ou une exception. Mais n'est encouragé que si vous implémentez clonable.

Janusz
la source
2
Vous ne pouvez pas appeler clone sur chaque objet, car il est protégé!
Pavel Minaev
2

À mon humble avis, c'est aussi simple que ceci:

  • #clone ne doit pas être appelé sur des objets non clonables, il n'est donc pas rendu public
  • #clonedoit être appelée par les sous-classes ob Objectqui implémentent Cloneable pour obtenir la copie superficielle de la bonne classe

Quelle est la bonne portée pour les méthodes qui doivent être appelées par des sous-classes, mais pas par d'autres classes?

C'est protected.

Les classes implémentant Cloneablebien sûr rendront cette méthode publique afin qu'elle puisse être appelée à partir d'autres classes.

Michaela Maura Elschner
la source
0

La méthode Clone () a une vérification interne `` instance de Cloneable ou non ''. C'est ainsi que l'équipe Java pourrait penser qu'elle limitera l'utilisation incorrecte de la méthode clone () method.clone () est protégée, c'est-à-dire accessible par les sous-classes uniquement. Puisque l'objet est la classe parente de toutes les sous-classes, la méthode Clone () peut être utilisée par toutes les classes en fait si nous n'avons pas la vérification ci-dessus de 'instance de Cloneable'. C'est la raison pour laquelle l'équipe Java aurait pu penser à restreindre l'utilisation incorrecte de clone () en vérifiant dans la méthode clone () 'is it instance of Cloneable'.

Par conséquent, toutes les classes implémentées clonables peuvent utiliser la méthode clone () de la classe Object.

De plus, comme il est protégé, il n'est disponible que pour les sous-classes qui implémentent une interface clonable. Si nous voulons la rendre publique, cette méthode doit être remplacée par la sous-classe avec sa propre implémentation.

SARIKA
la source
-2

Oui, même problème que j'ai rencontré. Mais je le résous en implémentant ce code

public class Side implements Cloneable {
    public Side clone() {

        Side side = null;
        try {
            side = (Side) super.clone();
        } catch (CloneNotSupportedException e) {
            System.err.println(e);
        }
        return side;
    }
}

Tout comme l'a dit quelqu'un avant.

fyhao
la source
1
CloneNotSupportedException est un autre exemple d'exception vérifiée qui doit être décochée (c'est-à-dire qu'elle doit étendre RuntimeException, pas Exception). Bien que la méthode clone () de la classe Side implémente Cloneable et ne lancera donc jamais CloneNotSupportedException, Side.clone () doit quand même attraper ou déclarer l'exception. Cela ajoute simplement un bruit de gestion des exceptions superflu à la méthode clone ().
Derek Mahar
-2

Eh bien, les développeurs Sun ne sont également qu'humains, et ils ont en effet commis une énorme erreur en implémentant la méthode de clonage comme protégée, la même erreur en implémentant une méthode de clonage qui ne fonctionnait pas dans ArrayList! Donc, en général, il existe un malentendu beaucoup plus profond des programmeurs Java même expérimentés sur la méthode de clonage.

Cependant, j'ai récemment trouvé une solution rapide et facile pour copier n'importe quel objet avec tout son contenu, peu importe comment il est construit et ce qu'il contient, voir ma réponse ici: Bug dans l'utilisation d'Object.clone ()

marque
la source
-3

Encore une fois, le framework Java JDK montre une pensée brillante:

L'interface clonable ne contient pas de "clone T public ();" parce qu'elle agit plus comme un attribut (par exemple. Serializable) qui permet à une instance d'être clonée.

Il n'y a rien de mal avec cette conception car:

  1. Object.clone () ne fera pas ce que vous voulez avec votre classe personnalisée.

  2. Si vous avez Myclass implémente Cloneable => vous remplacez clone () par "public MyClass clone ()"

  3. Si vous avez MyInterface étend Cloneable et certaines MyClasses implémentant MyInterface: définissez simplement "public MyInterface clone ();" dans l'interface et chaque méthode utilisant des objets MyInterface pourra les cloner, quelle que soit leur classe MyClass.

Victor
la source
2
si votre classe est héritée, votre implémentation de clone de classe dérivée ne sera pas sûre tant que vos implémentations de classe de base ne fourniront pas de méthodes de clonage sûres. En outre, il est une sorte de condition de conception inhabituelle d'avoir une interface sans méthode / attribut. Cette interface ne force pas la classe à implémenter le clonage.
prap19