J'ai besoin d'implémenter un clone profond dans l'un de mes objets qui n'a pas de superclasse.
Quelle est la meilleure façon de gérer le CloneNotSupportedException
jeté vérifié par la superclasse (qui est Object
)?
Un collègue m'a conseillé de le gérer de la manière suivante:
@Override
public MyObject clone()
{
MyObject foo;
try
{
foo = (MyObject) super.clone();
}
catch (CloneNotSupportedException e)
{
throw new Error();
}
// Deep clone member fields here
return foo;
}
Cela me semble être une bonne solution, mais je voulais la lancer à la communauté StackOverflow pour voir s'il y a d'autres informations que je peux inclure. Merci!
Cloneable
alors lancer unAssertionError
plutôt qu'un simpleError
est un peu plus expressif.Réponses:
Devez-vous absolument utiliser
clone
? La plupart des gens conviennent que Javaclone
est cassé.Josh Bloch sur la conception - Constructeur de copie versus clonage
Vous pouvez lire plus de discussion sur le sujet dans son livre Effective Java 2nd Edition, Item 11: Override
clone
judicieusement . Il recommande à la place d'utiliser un constructeur de copie ou une fabrique de copies.Il a continué en écrivant des pages de pages sur la façon dont, si vous pensez que vous devez, vous devriez mettre en œuvre
clone
. Mais il a conclu avec ceci:L'accent était le sien, pas le mien.
Puisque vous avez clairement indiqué que vous n'aviez pas d'autre choix que de mettre en œuvre
clone
, voici ce que vous pouvez faire dans ce cas: assurez-vous queMyObject extends java.lang.Object implements java.lang.Cloneable
. Si tel est le cas, vous pouvez garantir que vous n'attraperez JAMAIS unCloneNotSupportedException
. LancerAssertionError
comme certains l'ont suggéré semble raisonnable, mais vous pouvez également ajouter un commentaire expliquant pourquoi le bloc catch ne sera jamais entré dans ce cas particulier .Alternativement, comme d'autres l'ont également suggéré, vous pouvez peut-être implémenter
clone
sans appelersuper.clone
.la source
super.clone()
dans leurs méthodes de clonage, une sous-classe n'aura généralement à remplacer queclone()
si elle ajoute de nouveaux champs dont le contenu devrait être cloné. Si une superclasse utilisenew
plutôt quesuper.clone()
, alors toutes les sous-classes doivent remplacerclone()
si elles ajoutent ou non de nouveaux champs.Parfois, il est plus simple d'implémenter un constructeur de copie:
Cela vous évite les problèmes de manipulation
CloneNotSupportedException
, fonctionne avec lesfinal
champs et vous n'avez pas à vous soucier du type à retourner.la source
La façon dont votre code fonctionne est assez proche de la manière «canonique» de l'écrire. Je jetterais un
AssertionError
dans la prise, cependant. Cela signale que cette ligne ne doit jamais être atteinte.la source
Cloneable
en général, c'est une idée cassée, comme expliqué dans Effective Java. Le PO a déjà exprimé qu'ils doivent utiliserCloneable
. Je n'ai donc aucune idée de la manière dont ma réponse pourrait être améliorée, sauf peut-être la supprimer purement et simplement.Il y a deux cas dans lesquels le
CloneNotSupportedException
sera lancé:Cloneable
(en supposant que le clonage réel soit finalement reporté àObject
la méthode de clonage de). Si la classe dans laquelle vous écrivez cette méthode implémenteCloneable
, cela ne se produira jamais (car toutes les sous-classes en hériteront de manière appropriée).Cloneable
.Ce dernier cas ne peut pas se produire dans votre classe (car vous appelez directement la méthode de la superclasse dans le
try
bloc, même si elle est invoquée depuis un appel de sous-classesuper.clone()
) et le premier ne devrait pas car votre classe devrait clairement implémenterCloneable
.Fondamentalement, vous devez enregistrer l'erreur à coup sûr, mais dans ce cas particulier, cela ne se produira que si vous gâchez la définition de votre classe. Traitez-le donc comme une version vérifiée de
NullPointerException
(ou similaire) - il ne sera jamais lancé si votre code est fonctionnel.Dans d'autres situations, vous devez être préparé à cette éventualité - il n'y a aucune garantie qu'un objet donné soit clonable, donc lorsque vous interceptez l'exception, vous devez prendre les mesures appropriées en fonction de cette condition (continuer avec l'objet existant, adopter une stratégie de clonage alternative par exemple sérialiser-désérialiser, lancer un
IllegalParameterException
si votre méthode nécessite le paramètre par clonable, etc. etc.).Edit : Bien que dans l'ensemble, je devrais souligner que oui, il
clone()
est vraiment difficile à implémenter correctement et difficile pour les appelants de savoir si la valeur de retour sera ce qu'ils veulent, doublement quand on considère les clones profonds vs peu profonds. Il est souvent préférable d'éviter complètement tout cela et d'utiliser un autre mécanisme.la source
super.clone()
.Utilisez la sérialisation pour effectuer des copies complètes. Ce n'est pas la solution la plus rapide mais cela ne dépend pas du type.
la source
Vous pouvez implémenter des constructeurs de copie protégée comme ceci:
la source
clone()
l'objet renvoyé par àgetMySecondMember()
moins qu'il n'ait unepublic clone
méthode.Même si la plupart des réponses ici sont valables, je dois dire que votre solution est également la manière dont les développeurs d'API Java le font. (Soit Josh Bloch ou Neal Gafter)
Voici un extrait de openJDK, classe ArrayList:
Comme vous l'avez remarqué et d'autres mentionnés,
CloneNotSupportedException
n'a presque aucune chance d'être jeté si vous avez déclaré que vous implémentez l'Cloneable
interface.En outre, vous n'avez pas besoin de remplacer la méthode si vous ne faites rien de nouveau dans la méthode remplacée. Vous ne devez le remplacer que lorsque vous devez effectuer des opérations supplémentaires sur l'objet ou que vous devez le rendre public.
En fin de compte, il est toujours préférable de l'éviter et de le faire d'une autre manière.
la source
la source
Ce n'est pas parce que l'implémentation java de Cloneable est cassée que vous ne pouvez pas en créer une.
Si le véritable objectif d'OP était de créer un clone profond, je pense qu'il est possible de créer une interface comme celle-ci:
puis utilisez le constructeur prototype mentionné précédemment pour l'implémenter:
et une autre classe avec un champ objet AClass:
De cette façon, vous pouvez facilement cloner en profondeur un objet de la classe BClass sans avoir besoin de @SuppressWarnings ou d'un autre code astucieux.
la source