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

139

Il est généralement entendu que l' Cloneableinterface en Java est cassée. Il y a de nombreuses raisons à cela, que je ne mentionnerai pas; d'autres l'ont déjà fait. C'est également la position des architectes Java eux-mêmes.

Ma question est donc la suivante: pourquoi n'est-il pas encore obsolète? Si l'équipe Java de base a décidé qu'elle est cassée, elle doit également avoir envisagé la dépréciation. Quelles sont leurs raisons de ne pas le faire (en Java 8, ce n'est toujours pas obsolète )?

Kao
la source
41
Cette question n'est pas "essentiellement basée sur l'opinion", comme beaucoup se sentent apparemment en droit de juger. Ceux qui n'ont qu'une opinion sur les raisons ne sont tout simplement pas qualifiés pour répondre. Cependant, il est vrai que vous n'avez qu'une faible chance d'obtenir une réponse faisant autorité ici. Il est également vrai que votre question ne porte pas sur un problème résoluble que vous avez, donc c'est au moins à la limite du hors sujet.
Marko Topolnik
6
@MarkoTopolnik Je suis d'accord qu'il y a des gens dans le monde qui pourraient fournir une réponse faisant autorité, mais je ne pense pas que ce soit le test que nous appliquons ici. La raison de la fermeture indique que "les réponses à cette question auront tendance à être presque entièrement basées sur des opinions". Je soupçonne que ce sera le cas ici, à moins que nous ayons beaucoup de chance.
Duncan Jones
2
Voici "Comment et quand" abandonner Oracle ... ( docs.oracle.com/javase/6/docs/technotes/guides/javadoc/… ) L'interface clonable peut tomber dans le cas "bogué ou hautement inefficace" mais c'est très ouvert aux opinions.
Maxx
8
@Duncan Je considère toujours qu'il n'est pas juste de porter un jugement sur la question en se basant sur mes hypothèses sur le manque de discipline de la part des répondants . Si un utilisateur ne connaît pas la raison pour laquelle on l'interroge, il n'a pas le droit d'abuser du répondeur pour présenter son opinion sur la question.
Marko Topolnik
4
@lexicore Oui, exactement --- et vous pouvez parier qu'ils ont soigneusement considéré cette option et, par implication, doivent avoir de bonnes raisons de ne pas la désapprouver. Leur propre critique Cloneableest largement connue.
Marko Topolnik

Réponses:

120

Il y a un bogue soumis en 1997 à la base de données de bogues Java concernant l'ajout de clone()méthode à Cloneable, donc ce ne serait plus inutile. Il a été fermé avec la 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 pas prendre de mesures autres que l'amélioration de la documentation de l'interface clonable actuelle . Voici le texte intégral de la recommandation:

Les API de clonage d'objets Java existantes posent problème. Il existe une méthode "clone" protégée sur java.lang.Object et une interface java.lang.Cloneable. L'intention est que si une classe veut permettre à d'autres personnes de la cloner, alors elle devrait prendre en charge l'interface clonable et remplacer la méthode de clonage protégé par défaut par une méthode de clonage public. Malheureusement, pour des raisons commodément perdues dans la nuit des temps, l'interface clonable ne définit pas de méthode de clonage.

Cette combinaison entraîne une certaine 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 comment Cloneable est censé fonctionner et ce que le clone est censé faire.

Malheureusement, ajouter une méthode "clone" à Cloneable serait un changement incompatible. Cela ne cassera pas la compatibilité binaire, mais cela cassera la compatibilité source. Des preuves anecdotiques suggèrent qu'en pratique, il existe un certain nombre de cas où les classes prennent en charge l'interface clonable mais ne parviennent pas à fournir une méthode de clonage public. Après discussion, TRC a recommandé à l'unanimité de ne PAS modifier l'interface clonable existante, en raison de l'impact de compatibilité.

Une autre proposition était d'ajouter une nouvelle interface java.lang.PubliclyCloneable pour refléter l'objectif initial de Cloneable. À une majorité de 5 contre 2, TRC a recommandé de ne pas le faire. La principale préoccupation était que cela ajouterait encore plus de confusion (y compris une confusion orthographique!) À une image déjà confuse.

TRC a recommandé à l'unanimité que nous ajoutions de la documentation supplémentaire à l'interface clonable existante pour mieux décrire comment elle est censée être utilisée et pour décrire les «meilleures pratiques» pour les implémenteurs.

Donc, bien que ce ne soit pas directement obsolète , la raison pour ne pas rendre Cloneable "obsolète" est que le Comité de révision technique a décidé que la modification de la documentation existante suffirait à rendre cette interface utile. Et c'est ce qu'ils ont fait. Jusqu'à Java 1.4, Cloneableétait documenté comme suit:

Une classe implémente l'interface clonable 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 clonable 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 clonable 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 de clonage d'Object sur une instance qui n'implémente pas l'interface clonable 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 du fait qu'il implémente cette interface. Même si la méthode de clonage est invoquée de manière réfléchie, il n'y a aucune garantie qu'elle réussira.

Kao
la source
3
et savez-vous pourquoi la cloneméthode était en Objectpremier lieu?
njzk2
8
@ njzk2 C'est une partie essentielle du mécanisme --- c'est la méthode qui fait la magie extralinguistique de bas niveau consistant à copier l'image de l'objet bit par bit.
Marko Topolnik
3
@Unheilig Cette magie est quelque chose que vous ne pouvez pas répliquer avec un constructeur de copie: Object#clone()produit une instance de la même classe que l'original sans que cette classe soit connue au moment de la compilation.
Marko Topolnik
4
@AVolpe: Cela ne résoudrait pas l'incompatibilité de source mentionnée dans la réponse "où les classes prennent en charge l'interface clonable mais ne parviennent pas à fournir une méthode de clonage public." En particulier, les classes fournissant une méthode de clone non publique seraient interrompues.
Louis Wasserman
2
Belle histoire. Voici un lien direct vers la base de données des bogues. J'y ai ajouté un peu plus d'histoire, et j'en ai cité des parties dans ma réponse .
Stuart marque
64

La réponse courte à "pourquoi n'est pas Cloneableobsolète?" (ou en fait, pourquoi n'est pas Xobsolète, pour tout le monde X) est qu'il n'y a pas eu beaucoup d'attention portée à leur dépréciation.

La plupart des éléments qui ont été récemment obsolètes l'ont été, car il existe un plan spécifique pour les supprimer. Par exemple, les méthodes addPropertyChangeListeneret removePropertyChangeListenerde LogManager ont été déconseillées dans Java SE 8 avec l'intention de les supprimer dans Java SE 9. (La raison en est qu'elles compliquent inutilement les interdépendances de modules.) En effet, ces API ont déjà été supprimées du développement initial de JDK 9 . construit. (Notez que les appels d'écouteur de changement de propriété similaires ont également été supprimés Pack200; voir JDK-8029806 .)

Aucun plan similaire n'existe pour pour Cloneableet Object.clone().

Une réponse plus longue impliquerait de discuter d'autres questions, telles que ce que l'on pourrait s'attendre à ce qu'il arrive à ces API, quels coûts ou avantages la plate-forme gagnerait-elle si elles é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,

  • Ceci est dangereux et vous devriez être au courant des risques de son utilisation ( par exemple: Thread.stop(), Thread.resume()et Thread.suspend()).

  • Cela va être supprimé dans une prochaine version

  • C'est obsolète et c'est une bonne idée pour vous d'utiliser quelque chose de différent (exemple: de nombreuses méthodes dans java.util.Date)

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

Cloneableet Object.clone()sont «cassés» dans le sens où ils présentent des défauts de conception et sont difficiles à utiliser correctement. Cependant, clone()c'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. La suppression du 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. Alors peut-être que le marquage Cloneablecomme "obsolète" ou "remplacé" ou quelque chose de similaire serait approprié. Cela indiquerait aux développeurs qu'ils veulent probablement chercher ailleurs, mais cela ne signifierait pas que le mécanisme de clonage pourrait être supprimé dans une prochaine version. Malheureusement, un tel marqueur n'existe pas.

Dans l'état actuel des choses, la «dépréciation» semble impliquer une suppression éventuelle - malgré le fait qu'un nombre infime de fonctionnalités obsolètes ait jamais été supprimée - 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é qui incitera les développeurs à utiliser à la place des mécanismes alternatifs.

METTRE À JOUR

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

Cloneable n'a pas de méthodes pour la même raison que Serializable n'en 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. Cloneable est donc né pour indiquer l'intention du programmeur.

Donc, en bref. Le but de Cloneable n'était pas d'indiquer que vous disposiez d'une méthode publique clone (). 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 ().

Marques Stuart
la source
3
Une belle réponse que vous avez ici monsieur. J'aime particulièrement le fait que vous ne jetiez pas simplement Object.clone()dans le feu juste parce que tout le monde le veut, mais que vous soyez prêt à raisonner et à en évoquer les bonnes choses.
icza
2
Cependant, clone () reste 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. J'avais l'impression qu'avec le correctif de 6428387, tous les chemins de code (clone, new / arrayCopy, Arrays.copyOf) aboutissaient aux mêmes intrinsèques. Quelque chose a-t-il changé récemment?
bestsss
2
@bestsss Je ne pense pas que ce array.clone()soit nécessairement plus rapide que toutes les alternatives. Du point de vue de l'API, c'est le moyen le plus concis de dupliquer un tableau. Arrays.copyOf(array, newlen)se rapproche, mais il nécessite un paramètre de longueur, qui est redondant si vous ne modifiez pas la longueur.
Stuart Marks
2
@Holger Oui pour autant que nous puissions voir, c'est la première suppression réelle d'une API depuis la version 1.1. Notez également que même si nous convenons que Thread.suspend()et Thread.stop()(no-arg) sont dangereux, ils ne seront probablement pas supprimés - ou modifiés pour lever une exception sans condition - car les gens les utilisent réellement! Ils sont probablement prêts à assumer le risque. L'un des facteurs atténuants avec les écouteurs de changement de propriété est qu'ils ont été utilisés très rarement, de sorte que l'impact de leur suppression est faible.
Stuart marque
2
@Holger Conceptuellement java.beanspourrait être rendu indépendant de java.desktoppuisque les beans ne sont qu'une API de bibliothèque pour les propriétés. Malheureusement, si vous creusez dans l'API de beans, il existe de nombreuses dépendances sur AWT. La mise en œuvre a encore plus. Il serait certainement possible de les extraire, mais cela semble être beaucoup plus de travail que, par exemple, de démêler l'exploitation forestière des haricots. Tout l'effort de modularisation consiste à faire ce démêlage; sans aucun doute, plus pourrait être fait, mais alors Jigsaw prendrait encore plus de temps.
Stuart marque
-1

pourquoi il n'est pas encore obsolète?

Parce que le JCP n'a pas jugé bon de le faire et ne le fera peut-être jamais. Leur demander. Vous demandez au mauvais endroit.

Quelles sont les raisons de conserver cette chose dans l'API Java

Personne ne supprimera jamais quoi que ce soit de l'API Java, en raison de l'exigence de compatibilité descendante. La dernière fois que cela s'est produit, c'était le changement du modèle d'événement AWT entre 1.0 et 1.1 en 1996/7.

Marquis de Lorne
la source
17
Ils ont (effectivement) supprimé Thread.stop(Throwable)en modifiant son contrat pour toujours lancer UnsupportedOperationExceptionà l'appelant (pas au thread cible!).
Marko Topolnik
22
Qu'est-ce qui s'est passé à peu près au même moment? La suppression de Thread.stop(Throwable)la fonctionnalité de 's s'est produite dans Java 8. Quoi qu'il en soit, le conseil inconditionnel de "leur demander" est faux car aujourd'hui l'architecte en chef Java lui-même est un membre actif de Stack Overflow. Il ne se donne tout simplement pas la peine de répondre à autre chose qu'à des questions liées à Streams.
Marko Topolnik
13
De plus, la question d'OP ne concerne pas la suppression , mais la dépréciation , et il est clair que la dépréciation se produit depuis le début.
Marko Topolnik
17
@EJP Je ne demande pas si Cloneablesera supprimé de l'API Java. Je demande pourquoi il ne sera pas supprimé. Et je pense que c'est un endroit parfait pour demander.
Kao
5
@VaheHarutyunyan Merci pour le cri, mais je ne suis pas un architecte Java. Je suis ingénieur dans le groupe JDK d'Oracle, qui gère ce genre de choses.
Stuart marque