2020/07/11

[筆記] Effective Java #13 審慎的覆寫 clone

Effective Java 3rd 簡體中文版筆記 #13 審慎的覆寫 clone
Cloneable 是個介面 (interface) 用來表示該物件允許 clone,但 Cloneable 裡卻又沒有 clone 方法可以覆寫,clone 方法其實定義在 Object 中。

 覆寫 clone 的規定,主要來自於 Object
  1. x.clone() != x 為 true
  2. x.clone().getClass() == x.getClass() 為 true
  3. x.clone().equals(x) 為 true (大部分情況)
如果要覆寫 clone 該怎麼寫? 應由 super.clone() 來獲得一份副本,然後在將它轉型。
public class PhoneNumber implements Cloneable {
    private String areaCode;
    private String lineNumb;
    ...
    
    @Override
    public PhoneNumber clone() {
        try {
            return (PhoneNumber) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
當 clone 的實體有可變物件存在時,就必須保證 clone 出來的副本,不會影響原本的。像是  clone 的實體有一陣列,單純只用 super.clone() 是不夠的,因為沒有建立新的陣列,副本中陣列的引用與原本的引用是一樣的。當原本的陣列有變動時,副本的陣列就會一起改變,正確的方式為對該陣列進行遞迴的 clone 動作。如果副本中有 final field,那麼就不適用,final field 無法進行第二次的賦值,這是根本的問題,應考慮是否將 final 關鍵字移除。在下面程式碼中,Room 中有陣列存在,需要遞迴的對物件作 clone
public class Room implements Cloneable {
    private String id;
    private Object[] devices;

    @Override
    public Room clone() {
        try {
            Room room = (Room) super.clone();
            room.devices = this.devices.clone();
            return room;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
如果 Room 中的 devices 又出現其它物件的引用,該怎麼辦? 這時候就會陷入深度拷貝 (deep copy) 的問題。物件拷貝更好的方式為提供拷貝建構子 (copy constructor) 拷貝工廠 (copy factory)。這兩種方法都比 clone 還好,因為不會受到 final 影響,不需要作深層的遞迴流程,兩種方法都可以再傳入其它參數,依開發者需求,提供更多的功能。舉例來說,物件中有一 List,實作用 LinkedList,但在進行拷貝時,想要用 ArrayList 來取代,在上面兩種方法中是可行的,但是如果用 clone 的話,是不可行的。
// copy constructor
public Yum(Yum) { ... }
// copy factory
public static Yum newInstance(Yum yum) { ... }
轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2020/07/effective-java-13-override-clone.html

沒有留言:

張貼留言