84

Java で clone() を実装することについて簡単にグーグルで検索したところ、 http ://www.javapractices.com/topic/TopicAction.do?Id=71 が見つかりました。

それには次のコメントがあります。

コピー コンストラクターと静的ファクトリ メソッドは、クローンの代替手段を提供し、実装がはるかに簡単です。

私がやりたいのは、ディープコピーを作成することだけです。clone() を実装することは非常に理にかなっているように思えますが、Google で高くランク付けされたこの記事には少し不安があります。

私が気づいた問題は次のとおりです。

コピー コンストラクターは Generics では機能しません。

コンパイルできない疑似コードを次に示します。

public class MyClass<T>{
   ..
   public void copyData(T data){
       T copy=new T(data);//This isn't going to work.    
   }
   ..
}

サンプル 1: ジェネリック クラスでコピー コンストラクターを使用する。

ファクトリ メソッドには標準名がありません。

再利用可能なコードのためのインターフェースがあるのはとてもいいことです。

public class MyClass<T>{
    ..
    public void copyData(T data){
        T copy=data.clone();//Throws an exception if the input was not cloneable
    }
    ..
}

サンプル 2: ジェネリック クラスで clone() を使用する。

clone は静的メソッドではないことに気付きましたが、保護されたすべてのフィールドの詳細なコピーを作成する必要があるのではないでしょうか? clone() を実装するとき、複製不可能なサブクラスで例外をスローするための余分な作業は、私には些細なことのように思えます。

何か不足していますか?任意の洞察をいただければ幸いです。

4

10 に答える 10

53

基本的に、クローンは壊れています。ジェネリックで簡単に機能するものはありません。あなたがこのようなものを持っている場合(ポイントを伝えるために短縮されています):

public class SomeClass<T extends Copyable> {


    public T copy(T object) {
        return (T) object.copy();
    }
}

interface Copyable {
    Copyable copy();
}

次に、コンパイラの警告を使用して、作業を完了できます。ジェネリックは実行時に消去されるため、コピーを実行するものには、キャストを生成するコンパイラ警告が含まれます。この場合、それは避けられません。。回避できる場合もありますが(ありがとう、kb304)、すべてではありません。インターフェイスを実装するサブクラスまたは不明なクラスをサポートする必要がある場合を考えてみます(たとえば、必ずしも同じクラスを生成しないコピー可能なコレクションを反復処理していた場合など)。

于 2009-07-09T20:03:05.630 に答える
25

Builderパターンもあります。詳細については、EffectiveJavaを参照してください。

あなたの評価がわかりません。コピーコンストラクターでは、型を完全に認識していますが、なぜジェネリックスを使用する必要があるのですか?

public class C {
   public int value;
   public C() { }
   public C(C other) {
     value = other.value;
   }
}

最近ここで同様の質問がありました。

public class G<T> {
   public T value;
   public G() { }
   public G(G<? extends T> other) {
     value = other.value;
   }
}

実行可能なサンプル:

public class GenTest {
    public interface Copyable<T> {
        T copy();
    }
    public static <T extends Copyable<T>> T copy(T object) {
        return object.copy();
    }
    public static class G<T> implements Copyable<G<T>> {
        public T value;
        public G() {
        }
        public G(G<? extends T> other) {
            value = other.value;
        }
        @Override
        public G<T> copy() {
            return new G<T>(this);
        }
    }
    public static void main(String[] args) {
        G<Integer> g = new G<Integer>();
        g.value = 1;
        G<Integer> f = g.copy();
        g.value = 2;
        G<Integer> h = copy(g);
        g.value = 3;
        System.out.printf("f: %s%n", f.value);
        System.out.printf("g: %s%n", g.value);
        System.out.printf("h: %s%n", h.value);
    }
}
于 2009-07-09T20:02:45.320 に答える
11

Java には、C++ と同じ意味でのコピー コンストラクターがありません。

引数として同じ型のオブジェクトを取るコンストラクターを持つことができますが、これをサポートするクラスはほとんどありません。(クローン対応台数未満)

汎用クローンの場合、クラスの新しいインスタンスを作成し、リフレクションを使用して元のフィールド (浅いコピー) をコピーするヘルパー メソッドがあります (実際にはリフレクションのようなものですが、より高速です)。

ディープ コピーの簡単な方法は、オブジェクトをシリアル化してから逆シリアル化することです。

ところで:私の提案は、不変オブジェクトを使用することです。そうすれば、それらを複製する必要はありません。;)

于 2009-07-09T19:59:53.670 に答える
6

以下は、多くの開発者が使用しないいくつかの短所です。Object.clone()

  1. メソッドを使用するには、インターフェイスの実装、メソッドとハンドルの定義、最後にオブジェクトの呼び出しとキャストなど、Object.clone()コードに多くの構文を追加する必要があります。Cloneableclone()CloneNotSupportedExceptionObject.clone()
  2. Cloneableインターフェイスにはclone()メソッドがありません。これはマーカー インターフェイスであり、メソッドはありません。それでもclone()、オブジェクトに対して実行できることを JVM に伝えるためだけに実装する必要があります。
  3. Object.clone()は保護されているため、独自のclone()間接的な呼び出しを提供する必要がありObject.clone()ます。
  4. Object.clone()コンストラクターを呼び出さないため、オブジェクトの構築を制御することはできません。
  5. clone()たとえば、子クラスにメソッドを記述している場合Person、そのすべてのスーパークラスでclone()メソッドを定義するか、別の親クラスから継承する必要があります。そうしないと、super.clone()チェーンが失敗します。
  6. Object.clone()浅いコピーのみをサポートするため、新しく複製されたオブジェクトの参照フィールドは、元のオブジェクトのフィールドが保持していたオブジェクトを引き続き保持します。これを克服するにはclone()、クラスが保持している参照をすべてのクラスに実装しclone()、以下の例のようにメソッドで個別に複製する必要があります。
  7. Object.clone()最終フィールドはコンストラクターを介してのみ変更できるため、最終フィールドを操作することはできません。私たちの場合、すべてのPersonオブジェクトを id で一意にしたい場合、はコンストラクターを呼び出さず、 finalフィールドは から変更できないObject.clone()ため、使用すると重複したオブジェクトが取得されます。Object.clone()idPerson.clone()

コピー コンストラクターはObject.clone()

  1. インターフェイスの実装や例外のスローを強制しないでください。ただし、必要な場合は確実に実行できます。
  2. キャストは必要ありません。
  3. 未知のオブジェクト作成メカニズムに依存する必要はありません。
  4. 親クラスが契約に従うことや何かを実装することを要求しないでください。
  5. 最終フィールドを変更できるようにします。
  6. オブジェクトの作成を完全に制御できるようにします。初期化ロジックを記述できます。

Java Cloning - Copy Constructor vs Cloningの詳細を読む

于 2017-01-11T18:09:47.113 に答える
1

通常、clone() は保護されたコピー コンストラクターと連携して動作します。これが行われるのは、コンストラクターとは異なり、clone() が仮想である可能性があるためです。

Derived from a super class Base のクラス本体では、

class Derived extends Base {
}

したがって、最も単純化すると、これに clone() を使用して仮想コピー コンストラクターを追加します。(C++ では、仮想コピー コンストラクターとして clone を Joshi が推奨しています。)

protected Derived() {
    super();
}

protected Object clone() throws CloneNotSupportedException {
    return new Derived();
}

推奨されているように super.clone() を呼び出したい場合はさらに複雑になり、これらのメンバーをクラスに追加する必要があります。これを試すことができます

final String name;
Address address;

/// This protected copy constructor - only constructs the object from super-class and
/// sets the final in the object for the derived class.
protected Derived(Base base, String name) {
   super(base);
   this.name = name;
}

protected Object clone() throws CloneNotSupportedException {
    Derived that = new Derived(super.clone(), this.name);
    that.address = (Address) this.address.clone();
}

さて、処刑の場合、あなたは得ました

Base base = (Base) new Derived("name");

そしてあなたはそうしました

Base clone = (Base) base.clone();

これは Derived クラス (上記のもの) の clone() を呼び出し、これは super.clone() を呼び出します - これは実装されている場合とされていない場合がありますが、呼び出すことをお勧めします。次に、実装は super.clone() の出力を保護されたコピー コンストラクターに渡します。このコンストラクターは Base を受け取り、最終的なメンバーをそれに渡します。

次に、そのコピー コンストラクターは、スーパークラスのコピー コンストラクターを呼び出し (存在することがわかっている場合)、ファイナルを設定します。

clone() メソッドに戻ると、非最終メンバーを設定します。

賢明な読者は、Base にコピー コンストラクターがある場合、それが super.clone() によって呼び出され、保護されたコンストラクターでスーパー コンストラクターを呼び出すと再度呼び出されることに気付くでしょう。スーパー コピー コンストラクターを 2 回。うまくいけば、リソースをロックしている場合、それがわかるでしょう。

于 2015-11-02T11:16:14.093 に答える
0

あなたのために働くかもしれない1つのパターンはBeanレベルのコピーです。基本的に、引数なしのコンストラクターを使用し、さまざまなセッターを呼び出してデータを提供します。さまざまなBeanプロパティライブラリを使用して、プロパティを比較的簡単に設定することもできます。これはclone()を実行することと同じではありませんが、多くの実用的な目的では問題ありません。

于 2009-07-09T20:13:12.740 に答える
-2

あなたが見逃しているのは、 clone はデフォルトと慣習によって浅いコピーを作成し、一般に深いコピーを作成することは現実的ではないということです。

問題は、どのオブジェクトがアクセスされたかを追跡できなければ、循環オブジェクト グラフのディープ コピーを実際に作成できないことです。clone() はそのような追跡を提供せず (.clone() へのパラメーターである必要があります)、したがって浅いコピーのみを作成します。

独自のオブジェクトがそのすべてのメンバーに対して .clone を呼び出したとしても、それはディープ コピーにはなりません。

于 2009-07-09T20:09:03.183 に答える