Cloneable 是個介面 (interface) 用來表示該物件允許 clone,但 Cloneable 裡卻又沒有 clone 方法可以覆寫,clone 方法其實定義在 Object 中。
覆寫 clone 的規定,主要來自於 Object:
- x.clone() != x 為 true
- x.clone().getClass() == x.getClass() 為 true
- x.clone().equals(x) 為 true (大部分情況)
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
沒有留言:
張貼留言