119

スーパークラスを持たないオブジェクトの1つにディープクローンを実装する必要があります。

CloneNotSupportedExceptionスーパークラス(である)によってスローされたチェックを処理するための最良の方法は何Objectですか?

同僚から、次のように処理するようにアドバイスされました。

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

    // Deep clone member fields here

    return foo;
}

これは私には良い解決策のように思えますが、StackOverflowコミュニティにそれを投げて、他に含めることができる洞察があるかどうかを確認したいと思いました。ありがとう!

4

9 に答える 9

127

絶対に使用する必要がありますcloneか?cloneほとんどの人は、Javaが壊れていることに同意します。

Josh BlochonDesign-コピーコンストラクターとクローン作成

clone私の本のクローン作成に関する項目を読んだ場合、特に行間を読んだ場合、私が深く壊れていると思うことがわかります。Cloneable[...]壊れているのは残念ですが、それは起こります。

このトピックに関する詳細については、彼の著書「Effective Java 2nd Edition、Item 11:Overrideclone慎重にオーバーライドする」を参照してください。代わりに、コピーコンストラクターまたはコピーファクトリを使用することをお勧めします。

彼は続けて、あなたがしなければならないと感じたら、あなたがどのように実装すべきかについてのページのページを書きましたclone。しかし、彼はこれで締めくくりました:

このすべての複雑さは本当に必要ですか?めったに。を実装するクラスを拡張する場合Cloneable、適切に動作するメソッドを実装する以外に選択肢はほとんどありませんclone。それ以外の場合は、オブジェクトコピーの代替手段を提供するか、単に機能を提供しない方がよいでしょう

強調は私のものではなく、彼のものでした。


実装する以外に選択肢がほとんどないことを明確にしたのでclone、この場合にできることは次のとおりですMyObject extends java.lang.Object implements java.lang.Cloneable。その場合は、決してキャッチしないことを保証できますCloneNotSupportedException。一部の人が示唆しているように投げることは合理的と思われますが、この特定のケースでAssertionErrorキャッチブロックが入力されない理由を説明するコメントを追加することもできます。


あるいは、他の人も示唆しているように、をclone呼び出さずに実装することもできますsuper.clone

于 2010-02-24T14:46:06.440 に答える
59

コピーコンストラクターを実装する方が簡単な場合もあります。

public MyObject (MyObject toClone) {
}

それはあなたに取り扱いの手間を省き、フィールドCloneNotSupportedExceptionで働き、finalそしてあなたは戻るタイプについて心配する必要はありません。

于 2010-02-24T14:47:05.720 に答える
12

コードが機能する方法は、コードを記述する「標準的な」方法にかなり近いものです。しかし、私AssertionErrorはキャッチ内に投げます。それは、その線に決して到達してはならないことを示しています。

catch (CloneNotSupportedException e) {
    throw new AssertionError(e);
}
于 2010-02-24T14:51:37.490 に答える
9

CloneNotSupportedExceptionがスローされるケースは2つあります。

  1. 複製されるクラスは実装されません(実際の複製が最終的にのcloneメソッドCloneableに従うと仮定します)。Objectこのメソッドを記述しているクラスが実装されている場合Cloneable、これは発生しません(サブクラスは適切に継承するため)。
  2. 例外は実装によって明示的にスローされます。これは、スーパークラスが。の場合にサブクラスのクローン化を防ぐための推奨される方法ですCloneable

try後者の場合はクラスで発生することはなく(サブクラスの呼び出しから呼び出された場合でも、ブロック内のスーパークラスのメソッドを直接呼び出しているsuper.clone()ため)、クラスは明らかに実装する必要があるため、前者の場合は発生しませんCloneable

基本的に、エラーを確実にログに記録する必要がありますが、この特定の例では、クラスの定義を台無しにした場合にのみ発生します。したがって、チェックされたバージョン(または同様のもの)のように扱いNullPointerExceptionます。コードが機能している場合はスローされません。


他の状況では、この不測の事態に備える必要があります-特定のオブジェクト複製可能であるという保証はありません。したがって、例外をキャッチするときは、この条件に応じて適切なアクションを実行する必要があります(既存のオブジェクトを続行し、別の複製戦略を実行します)例:serialize- IllegalParameterExceptiondeserialize、メソッドがクローン可能などのパラメータを必要とする場合はスローします。

編集:全体的にはそうですが、clone()正しく実装することは本当に難しく、呼び出し元が戻り値が希望どおりになるかどうかを知ることは困難です。深いクローンと浅いクローンを考えると、二重にそうなります。多くの場合、すべてを完全に回避し、別のメカニズムを使用する方がよいでしょう。

于 2010-02-24T14:51:28.257 に答える
6

シリアル化を使用してディープコピーを作成します。これは最速の解決策ではありませんが、タイプには依存しません。

于 2010-02-24T14:48:22.787 に答える
3

次のように、保護されたコピーコンストラクタを実装できます。

/* 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);
}
于 2010-02-24T15:04:08.213 に答える
1

ここでの答えのほとんどは有効ですが、あなたのソリューションは実際のJavaAPI開発者がそれを行う方法でもあることを伝える必要があります。(ジョシュ・ブロックまたはニール・ガフターのいずれか)

これは、openJDK、ArrayListクラスからの抜粋です。

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);
    }
}

お気づきのように、他の人が言及しているように、インターフェースCloneNotSupportedExceptionを実装することを宣言した場合、スローされる可能性はほとんどありません。Cloneable

また、オーバーライドされたメソッドで新しいことを何もしなければ、メソッドをオーバーライドする必要はありません。オブジェクトに対して追加の操作を行う必要がある場合、またはオブジェクトを公開する必要がある場合にのみ、オーバーライドする必要があります。

最終的には、それを回避し、他の方法を使用して実行するのが最善です。

于 2016-08-17T16:25:09.307 に答える
0
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) {

                }
        }
    }
}
于 2013-01-16T10:28:56.767 に答える
0

JavaのCloneableの実装が壊れているからといって、独自の実装を作成できないわけではありません。

OPの本当の目的がディープクローンを作成することだった場合、次のようなインターフェイスを作成することは可能だと思います。

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

次に、前述のプロトタイプコンストラクターを使用して実装します。

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);
    }
}

およびAClassオブジェクトフィールドを持つ別のクラス:

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);
    }
}

このようにして、@ SuppressWarningsやその他のギミックコードを必要とせずに、クラスBClassのオブジェクトを簡単にディープクローンできます。

于 2017-11-04T11:27:14.957 に答える