복제 방법을 올바르게 재정의하는 방법은 무엇입니까?
수퍼 클래스가없는 객체 중 하나에서 딥 클론을 구현해야합니다.
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 커뮤니티에 버리고 싶었습니다. 감사!
당신은 절대적으로 사용해야 clone
합니까? 대부분의 사람들은 Java clone
가 깨 졌다는 데 동의합니다 .
내 책에서 복제에 대한 항목을 읽은 경우, 특히 줄 사이를 읽으면 내가
clone
크게 망가 졌다고 생각 합니다. [...] 그것은 부끄러운Cloneable
일이지만 일어납니다.
그의 책 Effective Java 2nd Edition, Item 11 : Overrideclone
in 신중하게 주제에서 더 많은 토론을 읽을 수 있습니다 . 대신 복사 생성자 또는 복사 팩토리를 사용하는 것이 좋습니다.
그는 당신이 어떻게해야한다고 생각하는지 어떻게 구현해야하는지에 대한 페이지 페이지를 작성했습니다 clone
. 그러나 그는 이것으로 마감했다.
이 모든 복잡성이 정말로 필요한가? 드물게. 을 구현하는 클래스를 확장하면
Cloneable
선택의 여지가 없지만 올바르게 작동하는clone
메소드 를 구현할 수 있습니다 . 그렇지 않으면 대체 객체 복사 방법을 제공하거나 단순히 기능을 제공하지 않는 것이 좋습니다.
강조는 내 것이 아니라 그의 것이었다.
구현할 선택의 여지가 거의 없음을 분명히 했으므로이 clone
경우 수행 할 수있는 작업은 다음과 같습니다 MyObject extends java.lang.Object implements java.lang.Cloneable
. 이 경우 에는를 절대로 잡을 수 없습니다 CloneNotSupportedException
. 던지는 AssertionError
몇 가지 제안대로하는 것은 합리적인 것 같다,하지만 당신은 또한 catch 블록을 입력하지 않을 것입니다 이유를 설명하는 주석 추가 할 수있는 이 특별한 경우를 .
또는 다른 사람들도 제안했듯이을 clone
호출하지 않고 구현할 수 있습니다 super.clone
.
때로는 복사 생성자를 구현하는 것이 더 간단합니다.
public MyObject (MyObject toClone) {
}
처리 문제를 줄이고 필드와 CloneNotSupportedException
함께 작동하며 final
반환 할 유형에 대해 걱정할 필요가 없습니다.
코드가 작동하는 방식은 코드를 작성하는 "정식"방식과 매우 비슷합니다. AssertionError
그래도 캐치 내에 던질 것 입니다. 해당 라인에 도달해서는 안된다는 신호입니다.
catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
CloneNotSupportedException
던질 두 가지 경우 가 있습니다.
- 복제되는 클래스는 구현되지 않습니다
Cloneable
(실제 복제는 결국Object
의 복제 방법을 연기한다고 가정 ). 이 메소드를 작성하는 클래스가 implementsCloneable
에서는 서브 클래스가 적절하게 상속하기 때문에 이런 일이 발생하지 않습니다. - 예외는 구현에 의해 명시 적으로 발생합니다. 이것은 수퍼 클래스가있을 때 서브 클래스에서 복제 성을 방지하기 위해 권장되는 방법입니다
Cloneable
.
후자의 경우는 클래스에서 발생할 수 없으며 ( try
하위 클래스 호출에서 호출 된 경우에도 블록 에서 수퍼 클래스 '메서드를 직접 호출하므로) 클래스 super.clone()
는 클래스를 명확하게 구현해야하므로해서는 안됩니다 Cloneable
.
기본적으로 오류를 확실히 기록해야하지만이 특별한 경우 클래스 정의를 엉망으로 만드는 경우에만 발생합니다. 따라서 확인 된 버전 NullPointerException
(또는 유사한)으로 취급하십시오 -코드가 작동하는 경우 절대로 throw되지 않습니다.
In other situations you would need to be prepared for this eventuality - there is no guarantee that a given object is cloneable, so when catching the exception you should take appropriate action depending on this condition (continue with the existing object, take an alternative cloning strategy e.g. serialize-deserialize, throw an IllegalParameterException
if your method requires the parameter by cloneable, etc. etc.).
Edit: Though overall I should point out that yes, clone()
really is difficult to implement correctly and difficult for callers to know whether the return value will be what they want, doubly so when you consider deep vs shallow clones. It's often better just to avoid the whole thing entirely and use another mechanism.
Use serialization to make deep copies. This is not the quickest solution but it does not depend on the type.
You can implement protected copy constructors like so:
/* 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);
}
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) {
}
}
}
}
As much as the most of the answers here are valid, I need to tell that your solution is also how the actual Java API developers do it. (Either Josh Bloch or Neal Gafter)
Here is an extract from openJDK, ArrayList class:
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);
}
}
As you have noticed and others mentioned, CloneNotSupportedException
has almost no chance to be thrown if you declared that you implement the Cloneable
interface.
Also, there is no need for you to override the method if you don't do anything new in the overridden method. You only need to override it when you need to do extra operations on the object or you need to make it public.
Ultimately, it is still best to avoid it and do it using some other way.
Just because java's implementation of Cloneable is broken it doesn't mean you can't create one of your own.
If OP real purpose was to create a deep clone, i think that it is possible to create an interface like this:
public interface Cloneable<T> {
public T getClone();
}
then use the prototype constructor mentioned before to implement it:
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);
}
}
and another class with an AClass object field:
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);
}
}
In this way you can easely deep clone an object of class BClass without need for @SuppressWarnings or other gimmicky code.
참고URL : https://stackoverflow.com/questions/2326758/how-to-properly-override-clone-method
'Programing' 카테고리의 다른 글
Android에서 Progressive Web Apps의 기능과 기본 앱의 기능 및 그 반대로의 기능 (0) | 2020.08.03 |
---|---|
절의 알 수없는 열 (0) | 2020.08.03 |
Makefile에서 여러 줄 문자열 변수를 만들 수 있습니까? (0) | 2020.08.03 |
그룹화 된 팬더 데이터 프레임을 반복하는 방법? (0) | 2020.08.03 |
OpenCV에서 이미지를 선명하게하는 방법? (0) | 2020.08.03 |