Frage

Ich brauche einen tiefen Klon in einem meiner Objekte zu implementieren, die keine übergeordnete Klasse hat.

Was ist der beste Weg, um die geprüft CloneNotSupportedException von der übergeordneten Klasse geworfen zu behandeln (was Object)?

Ein Kollege hat mir geraten, es die folgende Art und Weise zu behandeln:

@Override
public MyObject clone()
{
    MyObject foo;
    try
    {
        foo = (MyObject) super.clone();
    }
    catch (CloneNotSupportedException e)
    {
        throw new Error();
    }

    // Deep clone member fields here

    return foo;
}

Dies scheint eine gute Lösung für mich, aber ich wollte es in die Gemeinschaft Stackoverflow werfen, um zu sehen, ob es noch andere Erkenntnisse sind, kann ich schließen. Dank!

War es hilfreich?

Lösung

Müssen Sie unbedingt clone verwenden? Die meisten Menschen sind sich einig, dass die Java clone gebrochen wird.

Josh Bloch auf Entwurf - Copy-Konstruktor im Vergleich zu Cloning

  

Wenn Sie den Artikel über das Klonen in meinem Buch gelesen haben, vor allem, wenn man zwischen den Zeilen lesen, werden Sie wissen, dass ich denke, clone tief gebrochen. [...] Es ist eine Schande, dass Cloneable gebrochen ist, aber es passiert.

Sie können mehr Diskussion über das Thema in seinem Buch lesen Effective Java 2nd Edition, Punkt 11: Aufschalten clone umsichtig . Er empfiehlt stattdessen eine Kopie Konstruktor oder kopieren Fabrik zu verwenden.

Er fuhr fort zu schreiben Seiten Seiten, wie, wenn Sie das Gefühl haben müssen, Sie clone implementieren sollten. Aber er mit diesem geschlossen:

  

Ist das alles Komplexitäten wirklich notwendig? Selten. Wenn Sie eine Klasse erweitern, dass Geräte Cloneable, Sie haben keine andere Wahl als ein gut erzogener clone Verfahren zu implementieren. Andernfalls Sie sind besser dran, alternative Mittel zum Objekt Vervielfältigungen oder einfach nicht die Fähigkeit, die Bereitstellung .

Der Schwerpunkt lag sein, nicht meine.


Da Sie machte deutlich, dass man kaum eine andere Wahl haben, aber clone zu implementieren, ist hier, was Sie in diesem Fall tun kann: Stellen Sie sicher, dass MyObject extends java.lang.Object implements java.lang.Cloneable. Wenn das der Fall ist, dann können Sie garantieren, dass Sie NIE ein CloneNotSupportedException fangen. Werfen AssertionError wie einige vorgeschlagen haben scheint vernünftig, aber Sie können auch einen Kommentar hinzufügen, der erklärt, warum der catch-Block wird nie eingegeben wird in diesem speziellen Fall .


Alternativ kann, wie andere haben auch vorgeschlagen, können Sie vielleicht clone implementieren, ohne super.clone aufrufen.

Andere Tipps

Manchmal ist es einfacher, eine Kopie Konstruktor zu implementieren:

public MyObject (MyObject toClone) {
}

Es erspart Ihnen die Mühe CloneNotSupportedException Handling, arbeitet mit final Feldern und Sie müssen sich keine Sorgen machen über die Art zurück.

Die Art und Weise Ihr Code funktioniert, ist ziemlich nahe an die „kanonische“ Art und Weise, es zu schreiben. Ich würde eine AssertionError im Fang werfen, though. Es signalisiert, dass diese Linie sollte nie erreicht werden.

catch (CloneNotSupportedException e) {
    throw new AssertionError(e);
}

Es gibt zwei Fälle, in denen die CloneNotSupportedException wird geworfen werden:

  1. Die Klasse geklont nicht implementiert Cloneable (unter der Annahme, dass die tatsächliche Klonen aufschiebt schließlich zu Object des Klon-Methode). Wenn die Klasse Sie diese Methode in Geräte Cloneable schreiben, wird dies nie passieren (da alle Unterklassen erben wird es entsprechend).
  2. Die Ausnahme explizit durch eine Implementierung ausgelöst wird -. Dies der empfohlene Weg ist Klonierbarkeit in einer Unterklasse zu verhindern, wenn die übergeordneten Klasse Cloneable ist

Der letztere Fall kann nicht in Ihrer Klasse auftreten (wie Sie direkt auf die Oberklasse Methode im try Block Aufruf sind, auch wenn sie aus einer Unterklasse Aufruf super.clone() aufgerufen) und der ehemalige sollte nicht da Ihre Klasse sollte klar Cloneable implementieren.

Grundsätzlich sollten Sie das Fehlerprotokoll sicher, aber in diesem speziellen Fall wird es nur, wenn Sie vermasseln Ihre Klasse Definition geschehen. So behandeln sie wie eine gesichtete Version NullPointerException (oder ähnlich) -. Es wird nie geworfen werden, wenn Ihr Code funktioniert


In anderen Situationen müßten Sie für diesen Fall vorbereitet sein - es gibt keine Garantie, dass ein bestimmtes Objekt ist klonbar, so dass, wenn die Ausnahme abfangen sollten Sie entsprechende Maßnahmen ergreifen unter dieser Bedingung abhängig (continue mit dem bestehenden Objekt, nehmen Sie eine alternative Klonierungsstrategie zB serialize-deserialize, einen IllegalParameterException werfen, wenn Ihre Methode, um die Parameter von klonbar usw. usw. erfordert.).

Bearbeiten : Obwohl insgesamt sollte ich darauf hinweisen, dass ja, clone() wirklich schwierig ist, richtig zu implementieren und zu schwer für Anrufer zu wissen, ob der Rückgabewert wird das sein, was sie wollen, doppelt so, wenn Sie tief betrachten vs flache Klone. Oft ist es besser, nur die ganze Sache völlig zu vermeiden und einen anderen Mechanismus verwenden.

Verwenden Sie Serialisierung tiefe Kopien zu machen. Dies ist nicht die schnellste Lösung, aber es hängt nicht von der Art.

Sie können geschützte Kopierkonstruktoren implementieren möchten so:

/* This is a protected copy constructor for exclusive use by .clone() */
protected MyObject(MyObject that) {
    this.myFirstMember = that.getMyFirstMember(); //To clone primitive data
    this.mySecondMember = that.getMySecondMember().clone(); //To clone complex objects
    // etc
}

public MyObject clone() {
    return new MyObject(this);
}
public class MyObject implements Cloneable, Serializable{   

    @Override
    @SuppressWarnings(value = "unchecked")
    protected MyObject clone(){
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            ByteArrayOutputStream bOs = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bOs);
            oos.writeObject(this);
            ois = new ObjectInputStream(new ByteArrayInputStream(bOs.toByteArray()));
            return  (MyObject)ois.readObject();

        } catch (Exception e) {
            //Some seriouse error :< //
            return null;
        }finally {
            if (oos != null)
                try {
                    oos.close();
                } catch (IOException e) {

                }
            if (ois != null)
                try {
                    ois.close();
                } catch (IOException e) {

                }
        }
    }
}

So viel wie die meisten Antworten hier gültig sind, muss ich sagen, dass Ihre Lösung ist auch, wie die aktuelle Java-API-Entwickler es tun. (Entweder Josh Bloch oder Neal Gafter)

Hier ist ein Auszug aus openJDK, Arraylist-Klasse:

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

Wie Sie bemerkt haben und andere erwähnt, CloneNotSupportedException hat fast keine Chance geworfen werden, wenn Sie erklärt, dass Sie die Cloneable-Schnittstelle implementieren.

Auch gibt es keine Notwendigkeit für Sie die Methode außer Kraft zu setzen, wenn Sie etwas tun, nicht neu in der überschriebenen Methode. Sie müssen es nur außer Kraft zu setzen, wenn Sie zusätzliche Operationen an dem Objekt tun müssen, oder Sie brauchen, um es öffentlich zu machen.

Letztlich ist es immer noch am besten, es zu vermeiden und tut es auf eine andere Weise verwendet wird.

Nur weil die Java-Implementierung von klonbar gebrochen ist es nicht, dass Sie nicht eine eigene erstellen können.

Wenn OP eigentlicher Zweck einen tiefen Klon zu schaffen war, glaube ich, dass es möglich ist, eine Schnittstelle wie diese zu erstellen:

public interface Cloneable<T> {
    public T getClone();
}

dann den Prototyp Konstruktor, bevor es zu implementieren erwähnt verwenden:

public class AClass implements Cloneable<AClass> {
    private int value;
    public AClass(int value) {
        this.vaue = value;
    }

    protected AClass(AClass p) {
        this(p.getValue());
    }

    public int getValue() {
        return value;
    }

    public AClass getClone() {
         return new AClass(this);
    }
}

und eine weitere Klasse mit einem AClass Objektfeld:

public class BClass implements Cloneable<BClass> {
    private int value;
    private AClass a;

    public BClass(int value, AClass a) {
         this.value = value;
         this.a = a;
    }

    protected BClass(BClass p) {
        this(p.getValue(), p.getA().getClone());
    }

    public int getValue() {
        return value;
    }

    public AClass getA() {
        return a;
    }

    public BClass getClone() {
         return new BClass(this);
    }
}

Auf diese Weise können Sie easely tiefen Klon ein Objekt der Klasse B-Klasse ohne Notwendigkeit @SuppressWarnings oder andere unecht Code.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top