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 是一個弱點,我認為人們應該意識到它的侷限性。