2010-12-01 11 views
6

私はJavaでサブクラスをクローンする必要がありますが、これが発生するコードのポイントでは、サブクラスの型はわかりません。スーパークラスのみです。これを行うための最良のデザインパターンは何ですか?Javaでサブクラスをクローニングする

例:

class Foo { 
    String myFoo; 
    public Foo(){} 
    public Foo(Foo old) { 
     this.myFoo = old.myFoo; 
    } 
} 

class Bar extends Foo { 
    String myBar; 
    public Bar(){} 
    public Bar(Bar old) { 
     super(old); // copies myFoo 
     this.myBar = old.myBar; 
    } 
} 

class Copier { 
    Foo foo; 

    public Foo makeCopy(Foo oldFoo) { 

     // this doesn't work if oldFoo is actually an 
     // instance of Bar, because myBar is not copied 
     Foo newFoo = new Foo(oldFoo); 
     return newFoo; 

     // unfortunately, I can't predict what oldFoo's the actual class 
     // is, so I can't go: 
     // if (oldFoo instanceof Bar) { // copy Bar here } 
    } 
} 

答えて

6

あなたがコピーしようとしているクラスのコントロールを持っている場合は、仮想メソッドが進むべき道である:

class Foo { 
    ... 
    public Foo copy() { 
     return new Foo(this); 
    } 
} 
class Bar extends Foo { 
    ... 
    @Override public Bar copy() { 
     return new Bar(this); 
    } 
} 

(理想的には効果的なクラスが抽象的かのどちらかにします

+0

いいえ、動作しません。これを試してください:Bar bar = new Bar(); Foo foo = bar; foo.copy()。 BarではなくFoo.copy()を呼び出します。コピー() – ccleve

+2

@ user237815私はそうは思わない。 'Bar.copy'が上書きされます(' @ Override'でチェックされます)。 'Foo.copy'。 –

+0

トムが正しいです。 'copy'の呼び出しは実行時に行われ、JVMはオブジェクトの型に基づいて適切な' copy'メソッドを呼び出します。 –

0

これを行う方法は、FooBarにメソッドnewInstance()を作成することです。どちらの実装も、正しい型のオブジェクトを作成して返すことができます。コピーコードは、オブジェクトのコピーを取得するためにoldFoo.newInstance()を使用する必要があることだけを知る必要があります。

+0

私には、newInstance()という名前は、コピー/クローンではなく、無関係な新しいオブジェクトを意味しています。 –

+0

私は答えを追加した後、このような反応が予想されます。メソッド名は、(コピー、クローン、getInstanceなどのように)表現したいコンテキスト/コンセプトに依存します。おそらくOPが使う予定のものではないでしょう。私にとって、newInstanceという名前は、newInstanceメソッドを実行しているオブジェクトの値で初期化できる新しいオブジェクトインスタンスを通知します。 (静的なnewInstanceメソッドとは対照的に) – rsp

0

オブジェクトを詳細に複製する必要がある場合は、Javaシリアル化を使用することをお勧めします。これは、オブジェクトがSerializableを実装する必要がありますが、元のオブジェクトへの参照が共有されていないまったく新しいクローンオブジェクトを作成することになります。

プライマリクラスにSerializableを実装させると、すべてのサブクラスが自動的にそれをサポートします。

最後に、これはByteArrayOutputStreamではなくPiped Streamを使用してメモリを少なくしてより高速に使用できるように更新される可能性がありますが、小さなオブジェクトがある場合には目立たないでしょう。

public static<T> T clone(T object) { 
    try { 
     ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 
     ObjectOutputStream out = new ObjectOutputStream(bOut); 
     out.writeObject(object); 
     out.close(); 

     ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); 
     T copy = (T)in.readObject(); 
     in.close(); 

     return copy; 
    } 
    catch(Exception e) { 
     throw new RuntimeException(e); 
    } 
} 
0

あなたの要件に厳密に合っているかどうかはわかりませんが、Factory patternをご覧ください。

3

Javaの中で最も標準的な方法はCloneableを実装してクローニングすることができる各クラスを作成し、そのクラスのappropiatelyクローニングを行い、公開バージョンでObject.clone()を上書きすることであろう(デフォルトでObject.clone()オブジェクトの簡易コピーを作成します) 。

多くの人がCloneable/Object.clone()が悪いと思うことに注意してください。

2

の正しい方法でクローン/クローン可能を実装した場合、にはスーパークラスの問題はありません。これはObject.clone()が非常に特殊なメソッドであるため、非常に簡単な説明です:Object.clone()は、呼び出されたのと同じ型のオブジェクトを常に返します。

@see http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#clone%28%29

だから、クローンとクローン化可能の正しい実装は次のようになります。

class Foo implements Clonable{ 
    String myFoo; 
    ... 
    Foo clone() { 
     try { 
     Foo clone = (Foo) super.clone(); 
     clone.myFoo = this.myFoo; 
     return clone; 
     } catch (CloneNotSupportedException e) { 
     throw new RuntimeException("this could never happen", e); 
     } 
    } 
} 

class Bar extends Foo { 
    String myBar; 
    ... 
    Bar clone() { 
     try { 
     Bar clone = (Bar) super.clone(); 
     clone.myBar = this.myBar(); 
     return clone; 
     } catch (CloneNotSupportedException e) { 
     throw new RuntimeException("this could never happen", e); 
     } 
    } 
} 

そして、複写機の実装は簡単です:

class Copier { 
    Foo foo; 

    /** 
    * @return return a Foo if oldFoo is of class Foo, return Bar if oldClass is of class Bar. 
    */ 
    public Foo makeCopy(Foo oldFoo) { 
    return oldFoo.clone(); 
    } 
} 
0

このパターン/ソリューションは、A使用していますコピーコンストラクタとオーバーライドされた型付き仮想関数の組み合わせを生成し、インスタンスを各スーパークラスに伝播します。

public class A { 
    private String myA; 

    public A(A a) { 
     myA = a.myA; 
    } 

    public A copy() { 
     return new A(this); 
    } 
} 

public class B extends A { 
    private String myB; 

    public B(B b) { 
     super(b); 
     myB = b.myB; 
    } 

    public B copy() { 
     return new B(this); 
    } 
} 

public class C extends B { 
    private String myC; 

    public C(C c) { 
     super(c); 
     this.myC = c.myC; 
    } 

    public C copy() { 
     return new C(this); 
    } 
} 
関連する問題