Pourquoi les gens ont-ils si peur d’utiliser clone () (sur les classes de collection et JDK)?

Un certain nombre de fois, j’ai fait valoir que l’utilisation de clone() n’est pas une mauvaise pratique. Oui, je connais les arguments. Bloch a dit que c’était mauvais. Il l’a effectivement fait, mais il a dit que l’implémentation de clone() était mauvaise. L’utilisation de clone, en revanche, surtout si elle est correctement implémentée par une bibliothèque approuvée, telle que le JDK, est correcte.

Juste hier, j’ai eu une discussion à propos d’ une réponse qui suggère simplement que l’utilisation de clone() pour ArrayList est correcte (et n’a pas de commentaires positifs pour cette raison, je suppose).

Si nous regardons l’ @author de ArrayList , nous pouvons voir un nom familier – Josh Bloch. Donc, clone() sur ArrayList (et d’autres collections) est parfaitement correct (regardez simplement leurs implémentations).

Même chose pour Calendar et peut-être la plupart des classes java.lang et java.util .

Alors, donnez-moi une raison pour ne pas utiliser clone() avec les classes JDK?

Alors, donnez-moi une raison pour ne pas utiliser clone () avec les classes JDK?

  • Étant donné une référence ArrayList , vous aurez besoin d’une vérification getClass pour vérifier qu’il ne s’agit pas d’une sous-classe de la classe JDK. Et maintenant quoi? Les sous-classes potentielles ne sont pas fiables. Une sous-classe aurait vraisemblablement un comportement différent.
  • Cela nécessite que la référence soit plus spécifique que List . Certaines personnes ne s’en préoccupent pas, mais l’opinion majoritaire est que c’est une mauvaise idée.
  • Vous devrez faire face à un casting, un casting dangereux à cela.

D’après mon expérience, le problème du clone () se pose sur les classes dérivées.

Disons, ArrayList implémente clone (), qui renvoie un object de ArrayList .

Supposons que ArrayList a une classe dérivée, à savoir MyArrayList . Ce sera un désastre si MyArrayList ne remplace pas la méthode clone (). (Par défaut, il hérite du code de ArrayList ).

L’utilisateur de MyArrayList peut s’attendre à ce que clone () renvoie un object de MyArrayList ; Cependant, ce n’est pas vrai.

C’est ennuyeux: si une classe de base implémente clone (), sa classe dérivée doit remplacer complètement le clone ().

Je vais répondre avec une citation de l’homme lui-même ( Josh Bloch sur Design – Copy Constructor versus Cloning ):

Il y a très peu de choses pour lesquelles j’utilise Cloneable . Je propose souvent une méthode de public clone sur des classes concrètes car les gens l’attendent.

Il ne peut pas être plus explicite que cela: clone() sur ses classes Collection Framework sont fournies car les gens l’attendent . Si les gens cessent de s’y attendre, il l’aurait volontiers jeté. L’une des manières de faire en sorte que les gens cessent de s’attendre à cela est d’éduquer les gens à cesser de l’utiliser et de ne pas préconiser son utilisation.

Bien sûr, Bloch lui-même a également dit (pas de citation exacte mais proche) “L’API est comme le sexe: faites une erreur et vous la soutenez pour la vie”. Tout public clone() ne peut probablement jamais être repris. Néanmoins, ce n’est pas une raison suffisante pour l’utiliser.

Afin de ne pas encourager d’autres développeurs moins expérimentés à implémenter Clone() eux-mêmes. J’ai travaillé avec de nombreux développeurs dont les styles de codage sont largement copiés à partir du code (parfois horrible) avec lequel ils ont travaillé.

Il n’applique pas si l’implémenteur fera une copie profonde ou superficielle.

L’utilisation de clone peut être très risquée si vous ne vérifiez pas l’implémentation de cette méthode de clonage … comme vous pouvez le supposer, un clone peut agir de différentes manières … clone superficiel ou profond … et certains développeurs peuvent ne pas toujours vérifier quel genre de clone ils vont récupérer …

Dans une grosse application, grosse équipe, c’est aussi risqué car lorsque le clonage est utilisé, si vous modifiez l’implémentation du clone, vous pouvez modifier le comportement de l’application et créer de nouveaux bogues … et vérifier partout où le clone a été appelé la même chose pour d’autres méthodes d’object comme les égales, toSsortingng …

En modifiant le clone d’une petite sous-classe A (du clone Deep to Shallow par exemple), si une instance de B a une référence à A et un clone profond, alors puisque les objects référencés dans A ne sont pas clonés, le clone B ne sera plus un clone profond (et il en va de même pour toute classe référençant une instance B). Il n’est pas facile de gérer la profondeur de vos méthodes de clonage.

Aussi, quand vous avez une interface (extension de Clonable) et de nombreuses implémentations (parfois!), Vous allez parfois vérifier certaines implémentations et voir que les clones sont profonds, et appeler un clone sur l’interface, avoir un clone profond et peut introduire des bugs …

Pensez qu’il pourrait être préférable d’implémenter pour chaque méthode une méthode shallowClone et une méthode deepClone, et sur des besoins très spécifiques, d’implémenter des méthodes personnalisées (par exemple vous voulez un clone et limitez la profondeur de ce clone à 2, faites une implémentation personnalisée pour cela) classes concernées).

Ne pensez pas qu’il est important d’utiliser une méthode de clonage sur une classe JDK car elle ne sera pas modifiée par un autre développeur, ou du moins pas souvent. Mais mieux vaut ne pas appeler clone sur les classes JDK si vous ne connaissez pas l’implémentation de la classe au moment de la compilation. Je veux dire qu’appeler le clone sur une ArrayList n’est pas une question, mais l’appeler sur une Collection pourrait être dangereux car une autre implémentation de Collection pourrait être introduite par un autre développeur (il peut même étendre une impl Collection) et rien ne vous dit que le clone de cette impl travaillera comme vous vous y attendez …

Si les types de votre collection sont modifiables, vous devez vous soucier de savoir si la collection elle-même sera clonée ou si les éléments seront également clonés, ce qui implémentera votre propre fonction. le conteneur sera cloné rendra les choses beaucoup plus claires.