Warum ist die clone () -Methode in java.lang.Object geschützt?

111

Was ist der spezifische Grund, clone()der als geschützt definiert ist java.lang.Object?

Alex N.
quelle

Antworten:

107

Die Tatsache, dass der Klon geschützt ist, ist äußerst zweifelhaft - ebenso wie die Tatsache, dass die cloneMethode nicht in der CloneableSchnittstelle deklariert ist .

Dies macht die Methode für das Erstellen von Kopien von Daten ziemlich nutzlos, da Sie nicht sagen können :

if(a instanceof Cloneable) {
    copy = ((Cloneable) a).clone();
}

Ich denke, dass das Design von Cloneablejetzt weitgehend als Fehler angesehen wird (Zitat unten). Normalerweise möchte ich in der Lage sein, Implementierungen einer Schnittstelle vorzunehmen,Cloneable aber nicht unbedingt die SchnittstelleCloneable (ähnlich der Verwendung von Serializable). Dies kann nicht ohne Reflexion geschehen:

ISomething i = ...
if (i instanceof Cloneable) {
   //DAMN! I Need to know about ISomethingImpl! Unless...
   copy = (ISomething) i.getClass().getMethod("clone").invoke(i);
}

Zitat aus Josh Blochs effektivem Java :
"Die klonbare Schnittstelle war als Mixin-Schnittstelle für Objekte gedacht, um anzukündigen, dass sie das Klonen erlauben. Leider erfüllt sie diesen Zweck nicht ... Dies ist eine sehr atypische Verwendung von Schnittstellen und keine zu emulierende ... Damit die Implementierung der Schnittstelle Auswirkungen auf eine Klasse hat, müssen sie und alle ihre Oberklassen einem ziemlich komplexen, nicht durchsetzbaren und weitgehend undokumentierten Protokoll folgen. "

oxbow_lakes
quelle
2
Wenn das Objekt nicht klonbar ist, löst Clone () von Object die CloneNotSupportedException aus. Sie müssen also klonbar sein, wenn Sie super.clone () aufrufen möchten (was dazu führt, dass Object.clone () aufgerufen wird). Ich sehe nicht, wie ein Objekt serialisiert werden kann, ohne Serializable zu implementieren.
Steve Kuo
1
"Ich denke, dass das Design von Cloneable jetzt größtenteils als Fehler angesehen wird." [Zitat benötigt]
Kevin Panko
Entschuldigung - das habe ich nicht angedeutet. Ich habe lediglich angedeutet, dass "gutes" Design darin besteht , eine Schnittstelle nicht zu erweitern Serializable- es liegt an den Implementierungen, zu entscheiden, ob sie implementiert werden sollen Serializable. Ich habe dies erweitert auf Cloneable- es ist nicht etwas, das eine Schnittstelle erweitern sollte -, aber eine Implementierung einer Schnittstelle ist frei zu sein Cloneable. Das Problem ist, wenn Sie einen Parameter vom Schnittstellentyp haben, fragen Sie ihn, ob er klonbar ist. aber dann kann man es nicht wirklich klonen!
oxbow_lakes
6
@ Kevin - Josh Blochs effektives Java pp45. "Die klonbare Schnittstelle war als Mixin-Schnittstelle für Objekte gedacht, um anzukündigen, dass sie das Klonen erlauben. Leider
erfüllt
Ebenfalls auf derselben Seite: "Dies ist eine sehr untypische Verwendung von Schnittstellen und keine zu emulierende" und "Damit die Implementierung der Schnittstelle Auswirkungen auf eine Klasse hat, müssen sie und alle ihre Oberklassen einer ziemlich komplexen, nicht durchsetzbares und weitgehend undokumentiertes Protokoll "
oxbow_lakes
30

Die klonbare Schnittstelle ist nur eine Markierung, die besagt, dass die Klasse das Klonen unterstützen kann. Die Methode ist geschützt, da Sie sie nicht für ein Objekt aufrufen sollten, sondern sie als öffentlich überschreiben können (und sollten).

Von der Sonne:

In der Klasse Object wird die Methode clone () als geschützt deklariert. Wenn Sie nur Cloneable implementieren, können nur Unterklassen und Mitglieder desselben Pakets clone () für das Objekt aufrufen. Damit eine Klasse in einem Paket auf die clone () -Methode zugreifen kann, müssen Sie sie überschreiben und als öffentlich deklarieren, wie unten beschrieben. (Wenn Sie eine Methode überschreiben, können Sie sie weniger privat, aber nicht privater machen. Hier wird die geschützte clone () -Methode in Object als öffentliche Methode überschrieben.)

Bill K.
quelle
Was in Ordnung ist, bis Sie Schnittstellen in den Mix bringen - versuchen Sie, eine unbekannte Implementierung vonSet
oxbow_lakes
@oxbow_lakes: aber vielleicht sind einige Implementierungen von Set nicht klonbar
newacct
3
Sie können nichts klonen, was die klonbare Schnittstelle nicht implementiert - es ist eine Markierung mit der Aufschrift "Diese Klasse ist ordnungsgemäß klonbar" - sehr ähnlich der serialisierbaren Schnittstelle. Übrigens gibt es eine Möglichkeit, Klassen über eine gut funktionierende Serialisierung zu klonen - googeln Sie so etwas wie "Java-Serialisierungsklon", und Sie werden wahrscheinlich eine Handvoll Möglichkeiten finden, um eine tiefe Kopie Ihres Objekts zu erhalten.
Bill K
4
Sie können nichts klonen, das die klonbare Schnittstelle nicht implementiert, aber nur weil etwas die klonbare Schnittstelle implementiert, heißt das nicht, dass Sie sie klonen können.
Michael Myers
1
@BuckCherry toString hat eine Standardimplementierung. Wenn Sie es als gut bezeichnen, passiert etwas Gutes und Sie erhalten eine Zeichenfolge zurück. equals hat eine Standardimplementierung (wie ==). Klon kann keine Standardimplementierung haben. Wenn Sie einen Klon für ein Objekt aufrufen, das es nicht implementiert hat, erhalten Sie kein angemessenes Verhalten. Das Klonen ist kompliziert und kann nicht automatisch durchgeführt werden (einige Objekte ohne Standardkonstruktoren können möglicherweise nicht generisch geklont werden), sodass sie standardmäßig etwas sicherer sind. Ich denke, es überhaupt nicht nötig gewesen zu sein, es auf Objekt zu setzen.
Bill K
7

cloneist geschützt, weil es etwas ist, das überschrieben werden sollte, damit es spezifisch für die aktuelle Klasse ist. Es wäre zwar möglich, eine öffentliche cloneMethode zu erstellen , die jedes Objekt klonen würde, dies wäre jedoch nicht so gut wie eine Methode, die speziell für die Klasse geschrieben wurde, die sie benötigt.

Andrew Hare
quelle
aber warum muss es dafür geschützt werden?
Janusz
3
Es ist geschützt, so dass Sie das Objekt nicht verwenden (es wird sowieso nur eine Ausnahme auslösen). Sie möchten, dass Sie es in einer Klasse überschreiben und dann öffentlich machen. (auch ein paar Mal unten beantwortet)
Bill K
1
Und nutzlos, wenn man Schnittstellen gemäß meinem Punkt unten betrachtet
oxbow_lakes
sollte überschrieben werden ist nicht sehr klar dann sollte es abstrakt sein und dann nicht in der Objektklasse, ich versuche schwer zu verstehen warum es so ist.
Kumar Abhishek
4

Die Clone-Methode kann nicht direkt für ein Objekt verwendet werden, weshalb sie von der Unterklasse überschrieben werden soll.

Natürlich könnte es öffentlich sein und nur eine entsprechende Ausnahme auslösen, wenn das Klonen nicht möglich ist, aber ich denke, das wäre irreführend.

Die Art und Weise, wie der Klon jetzt implementiert wird, lässt Sie darüber nachdenken, warum Sie den Klon verwenden möchten und wie Ihr Objekt geklont werden soll.

Silfverstrom
quelle
2

Es ist geschützt, da die Standardimplementierung eine flache Kopie aller Felder (einschließlich privater Felder) in Mitgliedsform erstellt und den Konstruktor umgeht . Dies ist nicht etwas, für das ein Objekt in erster Linie ausgelegt sein könnte (z. B. kann es die erstellten Objektinstanzen in einer gemeinsam genutzten Liste oder Ähnlichem verfolgen).

Aus dem gleichen Grund wird die Standardimplementierung von clone()ausgelöst, wenn das aufgerufene Objekt nicht implementiert wird Cloneable. Es ist eine potenziell unsichere Operation mit weitreichenden Konsequenzen, und daher muss sich der Autor der Klasse ausdrücklich anmelden.

Pavel Minaev
quelle
Tatsächlich löst die Standardimplementierung (im Objekt) eine Ausnahme gemäß den Dokumenten aus ...
Bill K
2
Nein, es wirft nicht nur. Aus JavaDoc: "Der Methodenklon für die Klasse Object führt eine bestimmte Klonoperation aus. Wenn die Klasse dieses Objekts die Schnittstelle Cloneable nicht implementiert, wird zunächst eine CloneNotSupportedException ausgelöst. Beachten Sie, dass alle Arrays die Implementierung der Schnittstelle Cloneable berücksichtigen. Andernfalls erstellt diese Methode eine neue Instanz der Klasse dieses Objekts und initialisiert alle Felder mit genau dem Inhalt der entsprechenden Felder dieses Objekts, wie durch Zuweisung. Der Inhalt der Felder wird selbst nicht geklont. "
Pavel
2

Aus dem Javadoc von klonbar.

* By convention, classes that implement this interface (cloneable) should override 
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.

* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface.  Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.

Sie könnten also Klon für jedes Objekt aufrufen, aber dies würde Ihnen die meiste Zeit nicht die gewünschten Ergebnisse oder eine Ausnahme geben. Wird aber nur empfohlen, wenn Sie klonbar implementieren.

Janusz
quelle
2
Sie können nicht für jedes Objekt einen Klon aufrufen, da dieser geschützt ist!
Pavel Minaev
2

IMHO ist es so einfach:

  • #clone darf nicht für nicht klonbare Objekte aufgerufen werden, daher wird es nicht veröffentlicht
  • #clonemuss von Unterklassen ob aufgerufen werden Object, die Cloneable implementieren, um die flache Kopie der richtigen Klasse zu erhalten

Was ist der richtige Bereich für Methoden, die von Unterklassen aufgerufen werden sollen, aber nicht von anderen Klassen?

Es ist protected.

Klassen, die Cloneablenatürlich implementieren , machen diese Methode öffentlich, damit sie von anderen Klassen aufgerufen werden kann.

Michaela Maura Elschner
quelle
0

Die Clone () -Methode verfügt über eine interne Prüfung 'Instanz von Cloneable or not'. Auf diese Weise könnte das Java-Team die missbräuchliche Verwendung der clone () -Methode einschränken. Die clone () -Methode ist geschützt, dh nur für Unterklassen zugänglich. Da object die übergeordnete Klasse aller Unterklassen ist, kann die Clone () -Methode von allen infact-Klassen verwendet werden, wenn die obige Prüfung der 'Instanz von Cloneable' nicht vorliegt. Dies ist der Grund, warum das Java-Team möglicherweise daran gedacht hat, die missbräuchliche Verwendung von clone () durch die Überprüfung der clone () -Methode "Ist es eine Instanz von Cloneable" einzuschränken?

Daher kann jede klonbare Klasse die clone () -Methode der Object-Klasse verwenden.

Da es geschützt ist, steht es nur den Unterklassen zur Verfügung, die eine klonbare Schnittstelle implementieren. Wenn wir es öffentlich machen wollen, muss diese Methode von der Unterklasse mit ihrer eigenen Implementierung überschrieben werden.

SARIKA
quelle
-2

Ja, das gleiche Problem, das ich getroffen habe. Aber ich löse es, indem ich diesen Code implementiere

public class Side implements Cloneable {
    public Side clone() {

        Side side = null;
        try {
            side = (Side) super.clone();
        } catch (CloneNotSupportedException e) {
            System.err.println(e);
        }
        return side;
    }
}

Genau wie vorher hat jemand gesagt.

Fyhao
quelle
1
CloneNotSupportedException ist ein weiteres Beispiel für eine aktivierte Ausnahme, die deaktiviert werden sollte (dh die RuntimeException sollte erweitert werden, nicht Exception). Obwohl die Methode clone () in der Klasse Side Cloneable implementiert und daher niemals CloneNotSupportedException auslöst, muss Side.clone () die Ausnahme weiterhin abfangen oder deklarieren. Dies fügt der clone () -Methode nur überflüssiges Rauschen bei der Ausnahmebehandlung hinzu.
Derek Mahar
-2

Nun, auch die Sun-Entwickler sind nur Menschen, und sie haben tatsächlich einen großen Fehler gemacht, um die Klonmethode als geschützt zu implementieren, den gleichen Fehler wie sie eine nicht funktionierende Klonmethode in ArrayList implementiert haben! Im Allgemeinen gibt es sogar ein viel tieferes Missverständnis selbst erfahrener Java-Programmierer über die Klonmethode.

Ich habe jedoch kürzlich eine schnelle und einfache Lösung gefunden, um jedes Objekt mit seinem gesamten Inhalt zu kopieren, unabhängig davon, wie es erstellt wurde und was es enthält. Meine Antwort finden Sie hier: Fehler bei der Verwendung von Object.clone ()

Kennzeichen
quelle
-3

Auch hier zeigt das Java JDK-Framework brillantes Denken:

Die klonbare Schnittstelle enthält keinen "öffentlichen T-Klon ()". Methode, weil sie sich eher wie ein Attribut verhält (z. B. serialisierbar), mit dem eine Instanz geklont werden kann.

An diesem Design ist nichts auszusetzen, weil:

  1. Object.clone () macht mit Ihrer benutzerdefinierten Klasse nicht das, was Sie wollen.

  2. Wenn Myclass Cloneable implementiert =>, überschreiben Sie clone () mit "public MyClass clone ()".

  3. Wenn MyInterface Cloneable erweitert und einige MyClasses MyInterface implementieren: Definieren Sie einfach "public MyInterface clone ();" In der Schnittstelle und bei jeder Methode, die MyInterface-Objekte verwendet, können diese unabhängig von ihrer MyClass-Klasse geklont werden.

Sieger
quelle
2
Wenn Ihre Klasse vererbt wird, ist Ihre abgeleitete Klassenklonimplementierung erst dann sicher, wenn Ihre Basisklassenimplementierungen sichere Klonmethoden bereitstellen. Es ist auch eine ungewöhnliche Entwurfsbedingung, eine Schnittstelle ohne Methode / Attribut zu haben. Diese Schnittstelle erzwingt keine Klasse zum Implementieren des Klons.
Prap19