15

継承に関してclone()使用されるJavaのメソッドに関する簡単な質問があります-ボタンからずっと親クラスのメソッドを呼び出します。super.clone()clone()

メソッドはこのclone()オブジェクトのコピーを返すはずですが、継承階層に 3 つのクラスがあり、super.clone()3 回呼び出した場合、クラス Object のすぐ下にある継承階層の最上位クラスがそのクラスのコピーを取得しないのはなぜですか?戻ってきた?

A、B、C の 3 つのクラスがあるとします。ここで、A -> B -> C (inherit = ->)

次にsuper.clone()、クラス C を呼び出しclone()、B を呼び出してsuper.clone()を呼び出しclone()、A を呼び出して、super.clone()「今回は Object.clone() が呼び出されます」と呼び出します。thisから返されるクラス A に関するオブジェクトのコピーではないのはなぜObject.clone()ですか? それは私には論理的に聞こえます。

4

5 に答える 5

16

ここでは、少なくとも 2 つの問題が発生しているようです。

  1. clone() が通常どのように実装されるかについて混乱しているようです。

  2. クローン作成は良いアイデアだと考えているようです (コピー コンストラクター、ファクトリ、またはそれらに相当するものを使用するのではなく)。

clone メソッドの実装例を次に示します。

@Override 
public Object clone() throws CloneNotSupportedException {   
    //get initial bit-by-bit copy, which handles all immutable fields
    Fruit result = (Fruit)super.clone();

    //mutable fields need to be made independent of this object, for reasons
    //similar to those for defensive copies - to prevent unwanted access to
    //this object's internal state
    result.fBestBeforeDate = new Date( this.fBestBeforeDate.getTime() );

    return result;
}

super.clone()の結果はすぐに にキャストされることに注意してくださいFruit。これにより、継承メソッドは Fruit 固有のメンバー データを変更できます (fBestBeforeDateこの場合)。

したがって、子clone()メソッドへの呼び出しは、親のクローンを呼び出しますが、新しく作成されたコピーに独自の特定の変更も追加します。この場合、出てくるのはFruitではなくObjectです。

さらに重要なことに、クローンは悪い考えです。コピー コンストラクターとファクトリは、はるかに直感的で保守が容易な代替手段を提供します。例に添付したJava Practicesリンクのヘッダーを読んでみてください。いくつかの問題が要約されています。Josh Bloch も、より長い議論を行っています: クローンは絶対に避けるべきです。以下は、クローンが問題であると彼が考えている理由についての優れた要約の段落です。

オブジェクトの clone メソッドは非常にトリッキーです。これはフィールド コピーに基づいており、「言語外」です。コンストラクターを呼び出さずにオブジェクトを作成します。コンストラクターによって確立された不変条件が保持されるという保証はありません。Sun の内外で長年にわたって多くのバグがありました。これは、オブジェクトのクローンを作成するまでチェーンの中で super.clone を繰り返し呼び出すと、オブジェクトの浅いコピーが作成されるという事実に起因しています。クローンは通常、クローンされるオブジェクトと状態を共有します。その状態が変更可能な場合、2 つの独立したオブジェクトはありません。一方を変更すると、他方も変更されます。そして突然、ランダムな動作になります。

于 2012-08-10T16:38:31.547 に答える
4

これは特別なネイティブ メソッドです。これは、クローン作成を容易にするために行われました。そうしないと、祖先クラスのコード全体をコピーする必要があります。

于 2012-08-10T16:32:10.257 に答える
4

1 つの回答は受け入れられますが、質問の最初の部分 (サブクラスでのダウンキャストが常に機能する理由) の完全な回答ではないと思います。うまく説明できませんが、私と同じポスターの混乱の一部を解消できると思います。以下のクラスがあります

class A implements Cloneable 
{
   @Override
   protected A clone() throws CloneNotSupportedException // could be public
   { 
      Object clone = super.clone();
      System.out.println("Class A: " + clone.getClass()); // will print 'C'
      return (A) clone;
   }
}

class B extends A
{
   @Override
   protected B clone() throws CloneNotSupportedException
   { 
      A clone = super.clone();
      System.out.println("Class B: " + clone.getClass()); // will print 'C'
      return (B) clone;
   }
}

class C extends B
{
   @Override
   protected C clone() throws CloneNotSupportedException
   { 
      B clone = super.clone();
      System.out.println("Class C: " + clone.getClass()); // will print 'C'
      return (C) clone;
   }
}

static main(char[] argv)
{
   C c = new C();
   C cloned_c = c.clone();
}

この結果は、

Class A: C

Class B: C

Class C: C

コマンドラインに表示されます。したがって、実際のところ、 のclone()メソッドはコール スタックをObject調べ、呼び出されたチェーンの先頭にあるclone()オブジェクトのタイプを確認できます。次に、コールがバブルアップしてObject#clone()実際に呼び出された場合、そのタイプのオブジェクト創造された。したがって、これはすでにクラスで発生していCますが、これは奇妙ですが、ダウンキャストがClassCastException. 私は OpenJDK で確認しましたが、これはネイティブ コードに実装された Java ブラック マジックによってもたらされたようです。

于 2014-06-04T10:28:36.047 に答える
3

B の clone() が A の clone() が返すものを返し、C の clone() が B の clone() が返すものを返す場合、C の clone() は A の clone() が返すものを返します。

于 2012-08-10T16:30:51.497 に答える