web-dev-qa-db-fra.com

Pourquoi Cloneable n'est-il pas obsolète?

Il est communément admis que Cloneable interface en Java est cassé. Il y a plusieurs raisons à cela, que je ne mentionnerai pas; autres l'ont déjà fait C'est aussi la position de architectes Java eux-mêmes.

Ma question est donc: pourquoi n'a-t-il pas encore été déprécié? Si le noyau Java team a décidé qu'il est cassé, alors il doit également avoir envisagé la dépréciation. Quelles sont leurs raisons pour ne pas le faire (in Java 8 it est toujours pas obsolète )?

137
Kao

Il y a un bug soumis en 1997 à Java Bug Database sur l'ajout de la méthode clone() à Cloneable, donc ce ne serait plus inutile . Il a été clôturé par une résolution "ne résoudra pas" et la justification était la suivante:

Le comité d'examen technique (TRC) de Sun a longuement examiné ce problème et a recommandé de ne prendre aucune action autre que l'amélioration de la documentation de l'interface Cloneable actuelle . Voici le texte intégral de la recommandation:

Les API de clonage d'objet Java Java sont problématiques. Il existe une méthode de "clonage" protégée sur Java.lang.Object et une interface Java.lang.Cloneable. L'intention est que si une classe souhaite autoriser d'autres personnes à le cloner, il doit alors prendre en charge l'interface Cloneable et remplacer la méthode de clone protégé par défaut par une méthode de clonage publique. Malheureusement, pour des raisons commodément perdues dans la nuit des temps, l'interface Cloneable ne définit pas de méthode de clonage .

Cette combinaison entraîne une assez grande confusion. Certaines classes prétendent prendre en charge Cloneable, mais oublient accidentellement de prendre en charge la méthode de clonage. Les développeurs ne savent pas trop comment Cloneable est censé fonctionner et ce que le clone est censé faire.

Malheureusement, l'ajout d'une méthode "clone" à Cloneable serait un changement incompatible. Il ne cassera pas la compatibilité binaire, mais il cassera la compatibilité source. Des preuves anecdotiques suggèrent que dans la pratique, il existe un certain nombre de cas où les classes prennent en charge l'interface Cloneable mais ne fournissent pas de méthode de clonage publique. Après discussion, TRC a recommandé à l'unanimité de ne PAS modifier l'interface Cloneable existante, en raison de l'impact sur la compatibilité.

Une autre proposition consistait à ajouter une nouvelle interface Java.lang.PubliclyCloneable pour refléter l'objectif initialement prévu de Cloneable. À une majorité de 5 voix contre 2, le TRC a déconseillé cela. La principale préoccupation était que cela ajouterait encore plus de confusion (y compris la confusion orthographique!) À une image déjà confuse.

TRC a recommandé à l'unanimité d'ajouter de la documentation supplémentaire à l'interface Cloneable existante pour mieux décrire comment elle est destinée à être utilisée et pour décrire les "meilleures pratiques" pour les implémenteurs.

Ainsi, bien qu'il ne s'agisse pas directement de obsolète, la raison pour laquelle le clonable n'est pas "obsolète" est que le comité d'examen technique a décidé que la modification de la documentation existante serait suffisante assez pour rendre cette interface utile. Et c'est ce qu'ils firent. Jusqu'à Java 1.4, Cloneable était documenté comme suit:

Une classe implémente l'interface Cloneable pour indiquer à la méthode Object.clone () qu'il est légal pour cette méthode de faire une copie champ par champ des instances de cette classe.

Les tentatives de clonage d'instances qui n'implémentent pas l'interface Cloneable entraînent la levée de l'exception CloneNotSupportedException.

L'interface Cloneable ne déclare aucune méthode.

Depuis Java 1.4 (qui a été publié en février 2002) jusqu'à l'édition actuelle (Java 8), cela ressemble à ceci:

Une classe implémente l'interface Cloneable pour indiquer à la méthode Object.clone () qu'il est légal pour cette méthode de faire une copie champ par champ des instances de cette classe. L'appel de la méthode clone d'Object sur une instance qui n'implémente pas l'interface Cloneable entraîne la levée de l'exception CloneNotSupportedException.

Par convention, les classes qui implémentent cette interface doivent remplacer Object.clone (qui est protégé) par une méthode publique. Voir Object.clone () pour plus de détails sur la substitution de cette méthode.

Notez que cette interface ne contient pas la méthode clone. Par conséquent, il n'est pas possible de cloner un objet simplement en raison du fait qu'il implémente cette interface. Même si la méthode clone est invoquée de manière réfléchie, rien ne garantit qu'elle réussira.

119
Kao

La réponse courte à "pourquoi Cloneable n'est-il pas déconseillé?" (ou en effet, pourquoi X n'est pas déconseillé, pour tout X), c'est qu'il n'y a pas eu beaucoup d'attention à les déprécier.

La plupart des choses qui ont été dépréciées récemment l'ont été parce qu'il existe un plan spécifique pour les supprimer. Par exemple, les méthodes addPropertyChangeListener et removePropertyChangeListener de LogManager étaient déconseillées dans Java SE 8 avec l'intention de les supprimer dans Java SE 9. (La raison en est qu'ils compliquent inutilement les interdépendances des modules.) En effet, ces API ont déjà été supprimées du début du JDK 9 (notez que des appels d'écouteur de modification de propriété similaires ont également été supprimés de Pack200; voir JDK-8029806 .)

Il n'existe aucun plan similaire pour Cloneable et Object.clone().

Une réponse plus longue impliquerait de discuter d'autres questions, telles que ce que l'on pourrait attendre de ces API, quels coûts ou avantages augmenteraient la plate-forme s'ils étaient obsolètes et ce qui est communiqué aux développeurs lorsqu'une API est obsolète. J'ai exploré ce sujet dans ma récente conférence JavaOne, Debt and Deprecation . (Diapositives disponibles sur ce lien; vidéo ici .) Il s'avère que le JDK lui-même n'a pas été très cohérent dans son utilisation de la dépréciation. Il a été utilisé pour signifier plusieurs choses différentes, y compris par exemple,

  • C'est dangereux et vous devez être conscient des risques liés à son utilisation (exemple: Thread.stop(), Thread.resume() et Thread.suspend()).

  • Cela va être supprimé dans une future version

  • Ceci est obsolète et c'est une bonne idée pour vous d'utiliser quelque chose de différent (exemple: la plupart des méthodes de Java.util.Date)

Tous ces éléments sont des significations distinctes, et différents sous-ensembles d'entre eux s'appliquent à différentes choses qui sont obsolètes. Et certains sous-ensembles s'appliquent à des choses qui ne sont pas obsolètes (mais qui devraient peut-être être obsolètes).

Cloneable et Object.clone() sont "cassés" dans le sens où ils présentent des défauts de conception et sont difficiles à utiliser correctement. Cependant, clone() est toujours le meilleur moyen de copier des tableaux, et le clonage a une utilité limitée pour faire des copies d'instances de classes qui sont soigneusement implémentées. Supprimer le clonage serait un changement incompatible qui briserait beaucoup de choses. Une opération de clonage pourrait être réimplémentée d'une manière différente, mais elle serait probablement plus lente que Object.clone().

Cependant, pour la plupart des choses, un constructeur de copie est préférable au clonage. Il serait donc peut-être approprié de marquer Cloneable comme "obsolète" ou "remplacé" ou quelque chose de similaire. Cela indiquerait aux développeurs qu'ils souhaitent probablement chercher ailleurs, mais cela ne signifierait pas que le mécanisme de clonage pourrait être supprimé dans une future version. Malheureusement, aucun marqueur de ce type n'existe.

Dans l'état actuel des choses, la "dépréciation" semble impliquer une suppression éventuelle - en dépit du fait qu'un petit nombre de fonctionnalités obsolètes ont été supprimées - et la dépréciation ne semble donc pas justifiée pour le mécanisme de clonage. Peut-être qu'à l'avenir, un marquage alternatif pourra être appliqué pour inciter les développeurs à utiliser à la place des mécanismes alternatifs.

[~ # ~] mise à jour [~ # ~]

J'ai ajouté un historique supplémentaire au rapport de bogue . Frank Yellin, un des premiers implémenteurs JVM et co-auteur de la spécification JVM, a fait quelques commentaires en réponse au commentaire "perdu dans la nuit des temps" dans la recommandation TRC citée dans autre réponse . J'ai cité les parties pertinentes ici; le message complet est dans le rapport de bogue.

Cloneable n'a pas de méthodes pour la même raison que Serializable n'a pas. Cloneable indique une propriété de la classe, plutôt que de dire spécifiquement quoi que ce soit sur les méthodes prises en charge par la classe.

Avant la réflexion, nous avions besoin d'une méthode native pour faire une copie superficielle d'un objet. D'où Object.clone () est né. Il était également clair que de nombreuses classes voudraient remplacer cette méthode et que toutes les classes ne voudraient pas être clonées. Par conséquent, Cloneable est né pour indiquer l'intention du programmeur.

Bref. Le but de Cloneable n'était pas d'indiquer que vous disposiez d'une méthode clone () publique. C'était pour indiquer que vous étiez prêt à être cloné en utilisant Object.clone (), et c'était à l'implémentation de décider de rendre public ou non clone ().

63
Stuart Marks