슈퍼 클래스가없는 내 개체 중 하나에서 깊은 복제를 구현해야합니다.
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
하나요? 대부분의 사람들은 자바 clone
가 망가 졌다는 데 동의합니다 .
디자인에 대한 Josh Bloch-복사 생성자 대 복제
내 책에서 복제에 대한 항목을 읽었다면, 특히 줄 사이를 읽으면 내가
clone
깊이 깨 졌다고 생각한다는 것을 알게 될 것 입니다. […]Cloneable
부서진 것이 부끄러운 일이지만 일어납니다.
그의 저서 Effective Java 2nd Edition, Item 11 : Override of clone
judiciously 에서 주제에 대한 더 많은 토론을 읽을 수 있습니다 . 대신 복사 생성자 또는 복사 팩토리를 사용하는 것이 좋습니다.
그는 어떻게해야한다고 생각한다면 어떻게 구현해야하는지에 대한 페이지를 썼습니다 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
(또는 유사한 버전)처럼 취급하십시오 . 코드가 작동하면 절대로 던지지 않습니다.
다른 상황에서는 이러한 상황에 대비해야합니다. 주어진 객체 가 복제 가능 하다는 보장이 없으므로 예외를 포착 할 때이 조건에 따라 적절한 조치를 취해야합니다 (기존 객체를 계속 사용하고 대체 복제 전략을 사용하십시오). 예를 들어 serialize-deserialize, IllegalParameterException
메서드에 복제 가능 등의 매개 변수가 필요한 경우 throw합니다 .
편집 : 전반적으로 예를 지적해야하지만 clone()
실제로 구현하기가 어렵고 호출자가 반환 값이 원하는 값인지 여부를 알기 어렵습니다. 두 배로 딥 클론과 얕은 클론을 고려할 때. 모든 것을 완전히 피하고 다른 메커니즘을 사용하는 것이 더 낫습니다.
답변
직렬화 를 사용 하여 전체 복사본을 만듭니다. 이것은 가장 빠른 해결책은 아니지만 유형에 의존하지 않습니다.
답변
다음과 같이 보호 된 복사 생성자를 구현할 수 있습니다.
/* 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);
}
답변
여기에있는 대부분의 답변이 유효한만큼, 귀하의 솔루션은 실제 Java API 개발자가 수행하는 방식이기도합니다. (Josh Bloch 또는 Neal Gafter)
다음은 openJDK, ArrayList 클래스에서 발췌 한 것입니다.
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);
}
}
당신이 알아 차렸고 다른 사람들이 언급했듯이, 인터페이스 CloneNotSupportedException
를 구현한다고 선언하면은 던져 질 가능성이 거의 없습니다 Cloneable
.
또한, 재정의 된 메서드에서 새로운 작업을 수행하지 않으면 메서드를 재정의 할 필요가 없습니다. 객체에 대해 추가 작업을 수행해야하거나 공개로 설정해야하는 경우에만 재정의하면됩니다.
궁극적으로 그것을 피하고 다른 방법을 사용하는 것이 가장 좋습니다.