2010-12-01 4 views
6

자바에서 서브 클래스를 복제해야하지만, 코드가 발생하는 지점에서 서브 클래스 유형은 알 수 없으며 수퍼 클래스 만 알 수 있습니다. 이 작업을 수행하는 데 가장 적합한 디자인 패턴은 무엇입니까?자바에서 서브 클래스 복제하기

예 :

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

(이상적 클래스는 추상적 인 또는 효과적으로 중 하나를합니다

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

당신이 복사하려고하는 클래스의 제어 할 경우, 가상 방법은 앞으로 방법입니다 최종.)

+0

아니요, 작동하지 않습니다. 시도해보십시오 : Bar bar = new Bar(); Foo foo = bar; foo.copy(). Bar가 아닌 Foo.copy()를 호출합니다.복사() – ccleve

+2

@ user237815 나는 그렇게 생각하지 않는다. 'Bar.copy'가'Foo.copy'를 오버라이드합니다 ('@ Override'로 체크). –

+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

자바에서 가장 표준적인 방법은 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

이 패턴은/솔루션은 사용 복사 생성자와 재정의 된 유형화 된 가상 함수의 조합을 생성하고 인스턴스를 각 수퍼 클래스에 전파합니다.

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