# 13. clone 재정의는 주의해서 진행하라

<br>

### Cloneable의 문제점

* **Mixin 인터페이스의 실패:**
  * clone() 메서드가 선언된 곳 `Object`
  * 접근제어자 `protected`
    * 외부에서 직접 호출 불가
    * 리플렉션은 불안정
  * 이런 이유들로 `Cloneable` 구현만으로는 복제 기능 활용 불가
* **널리 쓰이는 방식:**
  * 문제점에도 불구하고 `Cloneable` 방식은 여전히 널리 사용되므로, 개발자는 그 동작 방식과 주의사항을 숙지할 필요가 있습니다.

<br>

### Cloneable 인터페이스의 특이한 역할

* **메서드 없는 인터페이스**
* **`clone()` 동작 방식:**
  * `Cloneable` 구현 클래스의 인스턴스에서 모든 필드를 복사한 객체 반환
  * 구현하지 않은 경우 `CloneNotSupportedException`
* **인터페이스의 역할 재정의:**
  * 인터페이스의 일반적인 역할 : 정의 기능 제공 선언
  * `Cloneable`은 상위 클래스(`Object`)의 `protected` 메서드 동작 방식을 변경
* **따라 하지 말 것**

<br>

### Object.clone() 메서드 명세의 허술함

* **모호한 복사 정의:**
  * `Object.clone()` 명세는 "복사"의 정확한 의미를 클래스의 구현에 의존.
* **느슨한 일반 규약:**
  * `x.clone() != x`, `x.clone().getClass() == x.getClass()`
  * `x.clone().equals(x) == true`
* **`super.clone()` 관례:**

  * `clone()` 메서드 내에서 `super.clone()` 호출은 관례, 강제 사항 X.
  * 하위 클래스의 복제 기능 손상 위험

  ```java
  class BaseClass implements Cloneable {
      private String baseName;
      //...
      @Override
      public BaseClass clone() {
        try {
            BaseClass cloned = (BaseClass) super.clone();
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
  }
  class SubClass extends BaseClass {
      private int subValue;
      //...
      @Override
      public SubClass clone() {
          SubClass cloned = (SubClass) super.clone(); //
          cloned.subValue = this.subValue;
          return cloned;
      }
  }
  ```

<br>

### 클래스 종류별 clone() 구현 방식

* **final 클래스:**
  * `super.clone()` 호출 불필요, `Cloneable` 구현 불필요
  * 생성자를 직접 호출하여 복제하는 것이 효율적일 수 있습니다.
* **상속 가능한 클래스:**
  * `super.clone()` 호출 (관례)
  * 깊은 복사 구현 필요 (가변 객체 필드)

<br>

### 공변 반환 타이핑 (Covariant Return Typing)

* **하위 클래스 반환 타입:**
  * Java 5부터 공변 반환 타이핑 지원으로, 하위 클래스에서 `clone()` 재정의 시 반환 타입을 하위 클래스 타입으로 지정 가능합니다.
* **형변환 불필요:**

  * 클라이언트 코드에서 불필요한 형변환을 줄여줍니다.

  ```java
  // Java 4 (JDK 1.4)
  class SubClass extends BaseClass {
      @Override
      public BaseClass clone() {
          // ...
          return super.clone();
      }
  }
  // 사용 시
  SubClass class = new SubClass();
  SubClass clonedClass = (SubClass) class.clone(); // 명시적 타입 변환 필요

  ```

  ```java
  // Java 5 (JDK 1.5) 이상
    class SubClass extends BaseClass {
      @Override
      public SubClass clone() { // SubClass 타입으로 반환 가능 (공변 반환 타이핑)
          // ...
          return (SubClass)super.clone();
      }
  }

    // 사용 시
    SubClass class = new SubClass();
    SubClass clonedClass = class.clone(); // 타입 변환 불필요!

  ```

<br>

### CloneNotSupportedException 예외 처리 비판

* **검사 예외의 부적절성:**
  * `CloneNotSupportedException`은 검사 예외이지만, `Cloneable` 구현 시 `super.clone()`은 항상 성공 하므로 비검사 예외로 설계되었어야 합니다.
* **불필요한 예외 처리:**
  * `Cloneable` 구현 클래스에서 `super.clone()` 호출 시 불필요한 `try-catch` 블록이 필요합니다.

    ```java
    @Override
    public BaseClass clone() {
        try {
            BaseClass cloned = (BaseClass) super.clone();
            System.out.println("BaseClass clone() called - Constructor used!");
            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
    ```

<br>

### 깊은 복사 구현 방법

* **재귀적 clone() 호출**

  * 가변 객체가 배열 또는 컬렉션인 경우, 내부 요소에 대해 재귀적으로 `clone()`을 호출하여 깊은 복사를 구현

  ```java
  public class Stack {
      private Object[] elements;

      //...
      @Override
      public Stack clone() throws CloneNotSupportedException{
          Stack result = (Stack) super.clone();
          result.elements = elements.clone();
          return result;
      }
  }
  ```
* **반복문:**

  * 복잡한 자료구조의 경우 반복문을 사용하여 깊은 복사를 구현

  ```java
  public class HashTable implements Cloneable {
      private Entry[] buckets = ...;

      private static class Entry {
          final Object key;
          Object value;
          Entry next;
          //...
      }
      //...

      private Entry deepCopy() {
          Entry result = new Entry(key, value, next);

          for (Entry p = result; p.next != null; p = p.next)
              p.next = new Entry(p.next.key, p.next.value, p.next.next);

          return return
      }

      @Override
      public HashTable clone() throws CloneNotSupportedException{
          HashTable result = (HashTable) super.clone();

          result.buckets = new Entry[buckets.length];
          for (int i = 0; i < buckets.length; i++)
              if (buckets[i] != null)
                  result.bucket[i] = buckets[i].deepCopy();
          return result;
      }
  }
  ```
* **고수준 API 활용:**
  * `super.clone()`으로 얕은 복사 후, 복제본 객체의 필드를 초기화하고 원본 객체의 상태를 복제하는 고수준 메서드를 호출하여 깊은 복사를 구현
    * HashTable의 `put(key, value)` 메서드 활용
  * 저수준 처리보다 낮은 성능
  * 전체 Cloneable 아키텍처와 어울리지 않는 방식

<br>

### final 필드와 Cloneable의 충돌

* **final 필드의 제약:**
  * `final` 필드는 생성자에서만 값 할당이 가능하므로, `clone()` 메서드에서 final 필드의 깊은 복사가 어려울 수 있습니다.
* **final 제약 완화:**
  * 복제 가능한 클래스를 만들기 위해 일부 `final` 제약자를 제거해야 할 수도 있습니다
  * 단, 원본과 복제본이 가변 객체를 공유해도 안전한 경우 제외

<br>

### 재정의 가능한 메서드 호출 주의

* **생성자와 clone()의 유사점:**
  * 생성자와 마찬가지로 `clone()` 메서드 내에서 재정의 가능한 메서드 호출을 피해야 합니다.
* **하위 클래스 상태 교정 문제:**
  * `clone()`이 하위 클래스에서 재정의한 메서드를 호출하면, 하위 클래스는 복제 과정에서 자신의 상태를 제대로 설정하지 못할 수 있습니다.
* **해결 방안:**
  * 복제 관련 메서드는 `final` 또는 `private`으로 선언하여 재정의를 막아야 합니다.

<br>

### 상속을 고려한 Cloneable 처리

* **상속용 클래스가 Cloneable 구현을 피하는 경우**

  ```java
  class BaseClass {
      private String data;

      public BaseClass(String data) {
          this.data = data;
      }
  }

  class SubClass extends BaseClass implements Cloneable {
      private int count;

      public SubClass(String data, int count) {
          super(data);
          this.count = count;
      }

      @Override
      public SubClass clone() throws CloneNotSupportedException {
          return (SubClass) super.clone(); // Object의 clone() 호출
      }
  }

  public class Main {
      public static void main(Stringargs) throws CloneNotSupportedException {
          SubClass original = new SubClass("test", 10);
          SubClass cloned = original.clone();
      }
  }
  ```

  * 하위 클래스가 필요에 따라 복제 기능을 추가
  * 복제에 대한 어떤 가정도 하지 않기 때문에 더 안전한 설계
  * 상속을 고려한 클래스는 `Cloneable` 구현을 피하는 것이 좋습니다.

<br>

* **Object 방식 모방**

  ```java
  class BaseClass {
      //...
      @Override
      protected Object clone() throws CloneNotSupportedException {
          return super.clone();
      }
  }

  class SubClass extends BaseClass implements Cloneable {
      //...
      @Override
      public SubClass clone() throws CloneNotSupportedException {
          return (SubClass) super.clone();
      }
  }
  ```

  * 상위 클래스에서 `Object`처럼 `protected clone()` 메서드를 제공하고 `CloneNotSupportedException`을 던지도록 선언
  * 하위 클래스에서 Cloneable을 구현하여 복제 기능을 선택적으로 추가 가능

<br>

* **clone() 퇴화**

  ```java
  class BaseClass {
      //...
      @Override
      protected final Object clone() throws CloneNotSupportedException {
          throw new CloneNotSupportedException();
          // or return null;
          // return this; //복제방지
      }
  }

  class SubClass extends BaseClass implements Cloneable {
      //...
  }
  ```

  * `clone()`을 동작하지 않도록 구현
  * `final`로 선언하여 하위 클래스에서 재정의를 방지

<br>

### 스레드 안전 클래스와 Cloneable

* **동기화 필요**
  * `Cloneable`을 구현한 스레드 안전 클래스는 `clone()` 메서드 역시 동기화
* **Object.clone() 비동기화**

  * `Object.clone()`은 동기화를 고려하지 않았으므로, `clone()` 재정의 시 명시적인 동기화 처리가 필요

  ```java
  @Override
  public synchronized ThreadSafeStack clone() throws CloneNotSupportedException {
      ThreadSafeStack result = (ThreadSafeStack) super.clone();
      result.elements = elements.clone();
      return result;
  }
  ```

<br>

### Clone 재정의 요약

* **필수 재정의:** `Cloneable` 구현 클래스는 반드시 `clone()`을 재정의.
  * 접근 제한자: `public`
  * 반환 타입: 클래스 자신
  * 구현 내용: `super.clone()` 호출 후 필요한 필드 수정 (깊은 복사)
* **깊은 복사**
  * 객체 내부의 모든 가변 객체를 복사하고, 복제본이 복사된 객체를 참조하도록 구현
* **기본 타입/불변 객체 필드:**
  * 수정 불필요 (단, 일련번호, 고유 ID 등은 수정 필요)

<br>

### Cloneable 대안: 복사 생성자와 복사 팩토리

* **복사 생성자:**

  * 동일 클래스 인스턴스를 인수로 받는 생성자

  ```java
  public class Color {
      private final int red;
      private final int green;
      private final int blue;

      // 복사 생성자
      public Color(Color original) {
          this.red = original.red;
          this.green = original.green;
          this.blue = original.blue;
      }
  }
  ```

  * 명시적 초기화
    * 모든 필드에 대한 초기화를 직접 제어
  * final 필드 호환
    * 생성자 내에서 final 필드 초기화 가능 (Cloneable은 final 필드 복제 불가)
  * 계층 구조 지원
    * 상속 시 하위 클래스에서 super() 호출로 기반 클래스 복제 가능
* **복사 팩토리:**

  * 복사 생성자를 모방한 정적 팩토리 메서드

  ```java
  public class NetworkConfig {
      private final String ip;
      private final int timeout;

      // 복사 팩토리
      public static NetworkConfig copyOf(NetworkConfig original) {
          return new NetworkConfig(original.ip, original.timeout);
      }
  }
  ```
* **Cloneable 대비 결정적 장점**

  | 특성           | Cloneable            | 복사 생성자/팩토리     |
  | ------------ | -------------------- | -------------- |
  | **객체 생성 제어** | JVM 의존적              | 생성자 직접 제어      |
  | **예외 처리**    | CheckedException 발생  | 예외 불필요         |
  | **final 필드** | 복제 불가                | 완벽 지원          |
  | **타입 안정성**   | 형변환 필요               | 타입 보장          |
  | **인터페이스 활용** | 구현 클래스에 종속           | 추상 타입으로 복제 가능  |
  | **상속 구조**    | super.clone() 관리 어려움 | 명시적 super() 호출 |

<br>

### 결론

* **Cloneable 확장/구현 지양:**
  * 새로운 인터페이스 확장 시 `Cloneable` 사용 금지
  * 새로운 클래스 `Cloneable` 구현 지양
* **final 클래스 예외적 허용:**
  * `final` 클래스의 `Cloneable` 구현은 성능 최적화 관점에서 신중히 고려 (위험성 적음)
* **복사 생성자/팩토리 권장:**
  * 객체 복사 기능 구현 시 복사 생성자 또는 복사 팩토리 사용 권장
  * 배열 clone() 예외 : 배열 복사는 `clone()` 메서드 방식이 가장 적합
