Java 作者談克隆方法的實現

今天在用 sonar 審覈代碼, 偶然看到下面的提示:
圖片描述

關於這個的提示大意是:

“克隆”不應該被覆蓋, 屬壞味道, 阻斷型錯誤
約書亞•布洛赫表示,許多人在 Java 中對 clone 方法 和 Cloneable 接口存在誤解,很大程度上是因爲重寫 clone 方法的規則很棘手, 且出錯難以糾正。
Object 的 clone 方法非常棘手。它基於屬性複製,而且是“超語言”。它創建一個對象而不調用構造函數。無法保證它保留構造函數創建的不變量。多年來,在 Sun 公司內外都存在許多錯誤,這源於這樣一個事實,即如果你只是反覆調用 super.clone 直到克隆了一個對象,那麼你就擁有了一個淺層的對象副本。克隆通常與正在克隆的對象共享狀態。如果該狀態是可變的,則您沒有兩個獨立的對象。如果您修改一個,另一個也會更改。突然之間,你會得到隨機行爲。

所以, 應該使用複製構造函數或複製工廠。

clone 無論是否實現 Cloneable 接口,此規則在被覆蓋時都會引發問題。

不合規的代碼示例

public class MyClass {
  // ...

  public Object clone() { // Noncompliant
    //...
  }
}

合規解決方案

public class MyClass {
  // ...

  MyClass (MyClass source) {
    //...
  }
}

參閱
《複製構造函數與克隆》
也可以參閱

S2157 - “Cloneables”應該實現“克隆”
S1182 - 覆蓋“clone”的類應爲“Cloneable”並調用“super.clone()”

下面爲引文翻譯

Josh Bloch 談設計
與《Effective Java》作者的對話,Josh Bloch

作者 Bill Venners
首次在JavaWorld上發表,2002年1月4日

複製構造函數與克隆

Bill Venners: 在你的書中,你建議使用複製構造函數而不是實現Cloneable和編寫clone。你能詳細說明嗎?

Josh Bloch:如果你已經閱讀了我的書中關於克隆的章節,特別是如果你看得仔細的話,你就會知道我認爲克隆已經完全壞掉的東西。有一些設計缺陷,其中最大的一個是 Cloneable 接口沒有 clone方法。這意味着它根本不起作用:實現了 Cloneable 接口並不說明你可以用它做什麼。相反,它說明了內部可能做些什麼。它說如果通過super.clone 反覆調用它最終調用 Object 的 clone 方法,這個方法將返回原始的屬性副本。

但它沒有說明你可以用一個實現 Cloneable 接口的對象做什麼,這意味着你不能做多態 clone 操作。如果我有一個 Cloneable 數組,你會認爲我可以運行該數組並克隆每個元素以製作數組的深層副本,但不能。你不能強制轉換對象爲 Cloneable 接口並調用 clone 方法,因爲 Cloneable 沒有public clone 方法,Object 類也沒有。如果您嘗試強制轉換 Cloneable 並調用該 clone 方法,編譯器會說您正在嘗試在對象上調用受保護的clone方法。

事實的真相是,您不通過實施 Cloneable 和提供 clone 除複製能力之外的公共方法爲您的客戶提供任何能力。如果您提供具有不同名稱的copy操作, 怎麼也不次於去實現 Cloneable 接口。這基本上就是你用複製構造函數做的事情。複製構造方法有幾個優點,我在本書中有討論。一個很大的優點是可以使副本具有與原始副本不同的實現。例如,您可以將一個 LinkedList 複製到 ArrayList。

Object 的 clone 方法是非常棘手的。它基於屬性複製,而且是“超語言”。它創建一個對象而不調用構造函數。無法保證它保留構造函數建立的不變量。多年來,在Sun內外存在許多錯誤,這源於這樣一個事實,即如果你只是super.clone 反覆調用鏈直到你克隆了一個對象,那麼你就擁有了一個淺層的對象副本。克隆通常與正在克隆的對象共享狀態。如果該狀態是可變的,則您沒有兩個獨立的對象。如果您修改一個,另一個也會更改。突然之間,你會得到隨機行爲。

我使用的東西很少實現 Cloneable。我經常提供實現類的 clone 公共方法,僅是因爲人們期望有。我沒有抽象類實現 Cloneable,也沒有接口擴展它,因爲我不會將實現的負擔 Cloneable 放在擴展(或實現)抽象類(或接口)的所有類上。這是一個真正的負擔,幾乎沒有什麼好處。

Doug Lea 走得更遠。他告訴我 clone 除了複製數組之外他不再使用了。您應該使用 clone 複製數組,因爲這通常是最快的方法。但 Doug 的類根本就不再實施 Cloneable了。他放棄了。而且我認爲這並非不合理。

這是一個恥辱, Cloneable 接口壞掉了,但它發生了。最初的 Java API在緊迫的期限內完成,以滿足市場窗口收緊的需求。最初的 Java 團隊做了不可思議的工作,但並非所有的 API 都是完美的。 Cloneable 是一個弱點,我認爲人們應該意識到它的侷限性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章