Warum ist Cloneable nicht veraltet?

139

Es ist allgemein bekannt, dass die CloneableSchnittstelle in Java defekt ist. Es gibt viele Gründe dafür, die ich nicht erwähnen werde; andere haben es schon getan. Es ist auch die Position der Java-Architekten selbst.

Meine Frage ist daher: Warum wurde noch nicht veraltet? Wenn das Java-Kernteam entschieden hat, dass es defekt ist, muss es auch die Ablehnung in Betracht gezogen haben. Was sind ihre Gründe dagegen (in Java 8 ist es immer noch nicht veraltet )?

Kao
quelle
41
Diese Frage ist nicht "primär meinungsbasiert", da sich viele offenbar berechtigt fühlen, zu urteilen. Diejenigen, die nur eine Meinung zu den Gründen haben, sind einfach nicht qualifiziert zu antworten. Es ist jedoch wahr, dass Sie nur eine entfernte Chance haben, hier eine maßgebliche Antwort zu erhalten. Es ist auch wahr, dass es sich bei Ihrer Frage nicht um ein lösbares Problem handelt, das Sie haben. Es handelt sich also zumindest um ein Grenzproblem außerhalb des Themas.
Marko Topolnik
6
@MarkoTopolnik Ich stimme zu, dass es einige Menschen auf der Welt gibt, die eine maßgebliche Antwort geben könnten, aber ich glaube nicht, dass dies der Test ist, den wir hier anwenden. Der Schließungsgrund besagt, dass "Antworten auf diese Frage in der Regel fast ausschließlich auf Meinungen beruhen". Ich vermute, dass dies hier der Fall sein wird, es sei denn, wir haben großes Glück.
Duncan Jones
2
Hier ist "Wie und Wann", um von Oracle abzulehnen ... ( docs.oracle.com/javase/6/docs/technotes/guides/javadoc/… ) Die klonbare Schnittstelle kann jedoch in den "fehlerhaften oder hoch ineffizienten" Fall fallen Es ist sehr offen für Meinungen.
Maxx
8
@Duncan Ich halte es immer noch nicht für fair, die Frage aufgrund meiner Annahmen über die mangelnde Disziplin der Antwortenden zu beurteilen . Wenn ein Benutzer den Grund, nach dem er gefragt wird, nicht kennt, ist er nicht berechtigt, die Antwortmöglichkeit zu missbrauchen, um seine Meinung zu dieser Angelegenheit zu äußern.
Marko Topolnik
4
@lexicore Ja, genau --- und Sie können wetten, dass sie diese Option gründlich in Betracht gezogen haben und implizit starke Gründe haben müssen, sie nicht zu verwerfen. Ihre eigene Kritik an Cloneableist weithin bekannt.
Marko Topolnik

Antworten:

120

Es gibt einen Fehler, der 1997 bei der Java Bug Database über das Hinzufügen von clone()Methoden zu gemeldet wurde Cloneable, sodass er nicht mehr unbrauchbar wäre. Es wurde mit der Entschließung "wird nicht behoben" geschlossen und die Begründung lautete wie folgt:

Das Technical Review Committee (TRC) von Sun hat dieses Problem ausführlich geprüft und empfohlen, keine anderen Maßnahmen als die Verbesserung der Dokumentation der aktuellen klonbaren Schnittstelle zu ergreifen . Hier ist der vollständige Text der Empfehlung:

Die vorhandenen APIs zum Klonen von Java-Objekten sind problematisch. Es gibt eine geschützte "Klon" -Methode für java.lang.Object und eine Schnittstelle für java.lang.Cloneable. Wenn eine Klasse anderen Personen das Klonen erlauben soll, sollte sie die klonbare Schnittstelle unterstützen und die standardmäßige geschützte Klonmethode mit einer öffentlichen Klonmethode überschreiben. Leider definiert die klonbare Schnittstelle aus Gründen, die im Nebel der Zeit verloren gehen, keine Klonmethode.

Diese Kombination führt zu ziemlich viel Verwirrung. Einige Klassen behaupten, Cloneable zu unterstützen, vergessen jedoch versehentlich, die Clone-Methode zu unterstützen. Entwickler sind verwirrt darüber, wie Cloneable funktionieren soll und was Clone tun soll.

Leider wäre das Hinzufügen einer "Klon" -Methode zu Cloneable eine inkompatible Änderung. Die Binärkompatibilität wird dadurch nicht beeinträchtigt, die Quellkompatibilität wird jedoch beeinträchtigt. Anekdoten weisen darauf hin, dass es in der Praxis eine Reihe von Fällen gibt, in denen Klassen die klonbare Schnittstelle unterstützen, jedoch keine öffentliche Klonmethode bereitstellen. Nach der Diskussion empfahl TRC einstimmig, die vorhandene klonbare Schnittstelle wegen der Auswirkungen auf die Kompatibilität NICHT zu ändern.

Ein alternativer Vorschlag bestand darin, eine neue Schnittstelle java.lang.PubliclyCloneable hinzuzufügen, um den ursprünglich beabsichtigten Zweck von Cloneable widerzuspiegeln. Mit einer Mehrheit von 5 zu 2 empfahl TRC dagegen. Das Hauptanliegen war, dass dies einem bereits verwirrten Bild noch mehr Verwirrung (einschließlich Rechtschreibverwirrung!) Veranlassen würde.

TRC empfahl einstimmig, der vorhandenen klonbaren Schnittstelle zusätzliche Dokumentation hinzuzufügen, um besser zu beschreiben, wie sie verwendet werden soll, und um "Best Practices" für Implementierer zu beschreiben.

Obwohl dies nicht direkt veraltet ist , liegt der Grund dafür, dass Cloneable nicht "veraltet" ist, darin, dass das Technical Review Comitee entschieden hat, dass die Änderung der vorhandenen Dokumentation ausreicht , um diese Schnittstelle nützlich zu machen. Und so taten sie es. Bis Java 1.4 Cloneablewurde wie folgt dokumentiert:

Eine Klasse implementiert die klonbare Schnittstelle, um der Object.clone () -Methode anzuzeigen, dass es für diese Methode zulässig ist, eine Feld-für-Feld-Kopie von Instanzen dieser Klasse zu erstellen.

Versuche, Instanzen zu klonen, die die klonbare Schnittstelle nicht implementieren, führen dazu, dass die Ausnahme CloneNotSupportedException ausgelöst wird.

Die Schnittstelle Cloneable deklariert keine Methoden.

Seit Java 1.4 (das im Februar 2002 veröffentlicht wurde) bis zur aktuellen Ausgabe (Java 8) sieht es folgendermaßen aus:

Eine Klasse implementiert die klonbare Schnittstelle, um der Object.clone () -Methode anzuzeigen, dass es für diese Methode zulässig ist, eine Feld-für-Feld-Kopie von Instanzen dieser Klasse zu erstellen. Das Aufrufen der Klonmethode von Object für eine Instanz, die die klonbare Schnittstelle nicht implementiert, führt dazu, dass die Ausnahme CloneNotSupportedException ausgelöst wird.

Gemäß der Konvention sollten Klassen, die diese Schnittstelle implementieren, Object.clone (das geschützt ist) mit einer öffentlichen Methode überschreiben. Weitere Informationen zum Überschreiben dieser Methode finden Sie unter Object.clone ().

Beachten Sie, dass diese Schnittstelle die Klonmethode nicht enthält. Daher ist es nicht möglich, ein Objekt nur aufgrund der Tatsache zu klonen, dass es diese Schnittstelle implementiert. Selbst wenn die Klonmethode reflektierend aufgerufen wird, gibt es keine Garantie dafür, dass sie erfolgreich ist.

Kao
quelle
3
und wissen Sie, warum die cloneMethode überhaupt war Object?
NJZK2
8
@ njzk2 Das ist ein wesentlicher Teil des Mechanismus - es ist die Methode, die die extralinguistische Magie auf niedriger Ebene ausführt, das Objektbild Bit für Bit zu kopieren.
Marko Topolnik
3
@Unheilig Diese Magie kann mit einem Kopierkonstruktor nicht repliziert werden: Object#clone()Erzeugt eine Instanz derselben Klasse wie das Original, ohne dass diese Klasse zur Kompilierungszeit bekannt ist.
Marko Topolnik
4
@AVolpe: Das würde die in der Antwort erwähnte "Inkompatibilität der Quelle" nicht beheben, wenn Klassen die klonbare Schnittstelle unterstützen, aber keine öffentliche Klonmethode bereitstellen. Insbesondere Klassen, die eine nicht öffentliche Klonmethode bereitstellen, würden beschädigt.
Louis Wasserman
2
Schöne Geschichte. Hier ist ein direkter Link zur Fehlerdatenbank. Ich habe dort etwas mehr Geschichte hinzugefügt und Teile davon in meiner Antwort zitiert .
Stuart Marks
64

Die kurze Antwort auf "Warum ist nicht Cloneableveraltet?" (oder in der Tat, warum wird es nicht Xveraltet X) ist, dass nicht viel darauf geachtet wurde, sie zu verwerfen.

Die meisten Dinge, die kürzlich veraltet waren, wurden veraltet, weil es einen bestimmten Plan gibt, sie zu entfernen. Zum Beispiel können die addPropertyChangeListenerund removePropertyChangeListenerMethoden der LogManager wurden als veraltet in Java SE 8 (ist der Grund , dass sie Modul Interdependenzen unnötig kompliziert.) Mit der Absicht, sie in Java SE 9. Entfernen der Tat haben diese APIs bereits von Anfang JDK 9 entfernt Entwicklung baut. (Beachten Sie, dass ähnliche Listener-Aufrufe zur Änderung von Eigenschaften auch aus entfernt wurden Pack200; siehe JDK-8029806 .)

Für Cloneableund gibt es keinen ähnlichen Plan Object.clone().

Eine längere Antwort würde die Erörterung weiterer Fragen beinhalten, z. B. was mit diesen APIs zu erwarten ist, welche Kosten oder Vorteile der Plattform entstehen würden, wenn sie veraltet wären, und was den Entwicklern mitgeteilt wird, wenn eine API veraltet ist. Ich habe dieses Thema in meinem letzten JavaOne-Vortrag " Schulden und Verfall" untersucht . (Folien unter diesem Link verfügbar; Video hier .) Es stellt sich heraus, dass das JDK selbst in seiner Verwendung von Verfall nicht sehr konsistent war. Es wurde verwendet, um verschiedene Dinge zu bedeuten, einschließlich zum Beispiel:

  • Dies ist gefährlich , und Sie sollten die Risiken der Verwendung es bewusst sein (Beispiel: Thread.stop(), Thread.resume(), und Thread.suspend()).

  • Dies wird in einer zukünftigen Version entfernt

  • Dies ist veraltet und es ist eine gute Idee, etwas anderes zu verwenden (Beispiel: viele der Methoden in java.util.Date)

All dies sind unterschiedliche Bedeutungen, und verschiedene Untergruppen davon gelten für verschiedene Dinge, die veraltet sind. Und einige Untergruppen davon gelten für Dinge, die nicht veraltet sind (die aber möglicherweise veraltet sein sollten).

Cloneableund Object.clone()sind "kaputt" in dem Sinne, dass sie Designfehler aufweisen und schwer richtig zu verwenden sind. Dies clone()ist jedoch immer noch der beste Weg, um Arrays zu kopieren, und das Klonen ist nur begrenzt nützlich, um Kopien von Instanzen von Klassen zu erstellen, die sorgfältig implementiert wurden. Das Entfernen des Klonens wäre eine inkompatible Änderung, die viele Dinge kaputt machen würde. Ein Klonvorgang könnte auf andere Weise erneut implementiert werden, wäre aber wahrscheinlich langsamer als Object.clone().

In den meisten Fällen ist jedoch ein Kopierkonstruktor dem Klonen vorzuziehen. Vielleicht Cloneablewäre es angebracht , sie als "veraltet" oder "ersetzt" oder ähnliches zu markieren . Dies würde Entwicklern mitteilen, dass sie wahrscheinlich woanders suchen möchten, aber es würde nicht signalisieren, dass der Klonmechanismus in einer zukünftigen Version möglicherweise entfernt wird. Leider existiert kein solcher Marker.

Aus heutiger Sicht scheint "Abwertung" eine eventuelle Entfernung zu bedeuten - trotz der Tatsache, dass eine verschwindend kleine Anzahl veralteter Features jemals entfernt wurde - und daher scheint eine Abwertung für den Klonmechanismus nicht gerechtfertigt zu sein. Vielleicht kann in Zukunft eine alternative Kennzeichnung angewendet werden, die Entwickler anweist, stattdessen alternative Mechanismen zu verwenden.

AKTUALISIEREN

Ich habe dem Fehlerbericht einen zusätzlichen Verlauf hinzugefügt . Frank Yellin, ein früher JVM-Implementierer und Mitautor der JVM-Spezifikation, gab einige Kommentare als Antwort auf den Kommentar "Verloren im Nebel der Zeit" in der in der anderen Antwort zitierten TRC-Empfehlung ab . Ich habe die relevanten Teile hier zitiert; Die vollständige Meldung befindet sich im Fehlerbericht.

Cloneable hat aus dem gleichen Grund keine Methoden wie Serializable. Klonbar gibt eine Eigenschaft der Klasse an, anstatt speziell etwas über die von der Klasse unterstützten Methoden zu sagen.

Vor der Reflexion benötigten wir eine native Methode, um eine flache Kopie eines Objekts zu erstellen. Daher wurde Object.clone () geboren. Es war auch klar, dass viele Klassen diese Methode überschreiben möchten und dass nicht jede Klasse geklont werden möchte. Daher wurde Cloneable geboren, um die Absicht des Programmierers anzuzeigen.

Kurz gesagt. Der Zweck von Cloneable bestand nicht darin, anzuzeigen, dass Sie eine öffentliche clone () -Methode hatten. Dies sollte darauf hinweisen, dass Sie bereit waren, mit Object.clone () geklont zu werden, und es lag an der Implementierung, zu entscheiden, ob clone () veröffentlicht werden soll oder nicht.

Stuart Marks
quelle
3
Eine gute Antwort haben Sie hier, Sir. Mir gefällt besonders, dass Sie nicht nur Object.clone()ins Feuer werfen, nur weil alle anderen es wollen, sondern auch bereit sind, zu argumentieren und die guten Dinge zur Sprache zu bringen.
icza
2
Clone () ist jedoch immer noch der beste Weg, um Arrays zu kopieren, und das Klonen ist nur begrenzt nützlich, um Kopien von Instanzen von Klassen zu erstellen, die sorgfältig implementiert wurden. Ich hatte den Eindruck, dass mit dem Fix von 6428387 alle Codepfade (clone, new / arrayCopy, Arrays.copyOf) die gleichen Eigenschaften aufwiesen. Hat sich in letzter Zeit etwas geändert?
Bests
2
@bestsss Ich denke nicht, dass array.clone()es notwendigerweise schneller ist als irgendwelche Alternativen. Aus API-Sicht ist dies die präziseste Methode zum Duplizieren eines Arrays. Arrays.copyOf(array, newlen)kommt nahe, erfordert jedoch einen Längenparameter, der redundant ist, wenn Sie die Länge nicht ändern.
Stuart Marks
2
@Holger Ja, soweit wir sehen können, ist dies das erste tatsächliche Entfernen einer API seit 1.1. Beachten Sie auch, dass Thread.suspend()und obwohl wir uns einig sind, dass und Thread.stop()(no-arg) gefährlich sind, sie wahrscheinlich nicht entfernt oder geändert werden, um eine Ausnahme bedingungslos auszulösen, weil die Leute sie tatsächlich verwenden! Vermutlich sind sie bereit, das Risiko zu tragen. Einer der mildernden Faktoren bei den Listenern für Eigenschaftsänderungen ist, dass sie sehr selten verwendet wurden, sodass die Auswirkungen ihrer Entfernung gering sind.
Stuart Marks
2
@Holger Konzeptionell java.beanskönnte unabhängig gemacht werden, java.desktopda Beans nur eine Bibliotheks-API für Eigenschaften sind. Wenn Sie sich in die Beans-API vertiefen, gibt es leider viele Abhängigkeiten von AWT. Die Implementierung hat noch mehr. Sicherlich könnte es möglich sein, sie zu befreien, aber dies scheint viel mehr Arbeit zu sein, als beispielsweise die Protokollierung von Bohnen zu entschlüsseln. Bei der gesamten Modularisierung geht es darum, diese Entflechtung durchzuführen. Zweifellos könnte mehr getan werden, aber dann würde Jigsaw noch länger dauern.
Stuart Marks
-1

warum ist es noch nicht veraltet?

Weil die JCP dies nicht für richtig gehalten hat und dies möglicherweise niemals tun wird. Frag sie. Du fragst am falschen Ort.

Was sind die Gründe dafür, dass dieses Ding in der Java-API bleibt?

Aufgrund der Abwärtskompatibilitätsanforderungen wird niemand jemals etwas aus der Java-API entfernen. Das letzte Mal geschah 1996/7 die Änderung des AWT-Ereignismodells zwischen 1,0 und 1,1.

Marquis von Lorne
quelle
17
Sie haben (effektiv) entfernt, Thread.stop(Throwable)indem sie ihren Vertrag so geändert haben UnsupportedOperationException, dass er immer zum Anrufer (nicht zum Ziel-Thread!) Wirft.
Marko Topolnik
22
Was geschah ungefähr zur gleichen Zeit? Das Entfernen der Thread.stop(Throwable)Funktionalität erfolgte in Java 8. Wie auch immer, der uneingeschränkte Rat, sie zu "fragen", ist falsch, da der Chef-Java-Architekt selbst heute ein aktives Mitglied bei Stack Overflow ist. Er beantwortet einfach nichts anderes als Streams-bezogene Fragen.
Marko Topolnik
13
Darüber hinaus geht es in der Frage von OP nicht darum Entfernen , sondern um die Abwertung , und es ist eindeutig die ganze Zeit über Abwertung eingetreten.
Marko Topolnik
17
@EJP Ich frage nicht, ob Cloneablevon der Java-API entfernt wird. Ich frage, warum es nicht abgelenkt wird. Und ich denke, dies ist ein perfekter Ort zum Fragen.
Kao
5
@ VaheHarutyunyan Danke für den Ruf, aber ich bin kein Java-Architekt. Ich bin Ingenieur in der JDK-Gruppe von Oracle, die dieses Zeug verwaltet.
Stuart Marks