為消除克隆能力,大家也許認為只需將clone()方法簡單地設為private(私有)即可,但這樣是行不通的,因為不能采用一個基礎類方法,並使其在衍生類中更“私有”。所以事情並沒有這麼簡單。此外,我們有必要控制一個對象是否能夠克隆。對於我們設計的一個類,實際有許多種方案都是可以采取的:
(1) 保持中立,不為克隆做任何事情。也就是說,盡管不可對我們的類克隆,但從它繼承的一個類卻可根據實際情況決定克隆。只有Object.clone()要對類中的字段進行某些合理的操作時,才可以作這方面的決定。
(2) 支持clone(),采用實現Cloneable(可克隆)能力的標准操作,並覆蓋clone()。在被覆蓋的clone()中,可調用super.clone(),並捕獲所有違例(這樣可使clone()不“擲”出任何違例)。
(3) 有條件地支持克隆。若類容納了其他對象的句柄,而那些對象也許能夠克隆(集合類便是這樣的一個例子),就可試著克隆擁有對方句柄的所有對象;如果它們“擲”出了違例,只需讓這些違例通過即可。舉個例子來說,假設有一個特殊的Vector,它試圖克隆自己容納的所有對象。編寫這樣的一個Vector時,並不知道客戶程序員會把什麼形式的對象置入這個Vector中,所以並不知道它們是否真的能夠克隆。
(4) 不實現Cloneable(),但是將clone()覆蓋成protected,使任何字段都具有正確的復制行為。這樣一來,從這個類繼承的所有東西都能覆蓋clone(),並調用super.clone()來產生正確的復制行為。注意在我們實現方案裡,可以而且應該調用super.clone()——即使那個方法本來預期的是一個Cloneable對象(否則會擲出一個違例),因為沒有人會在我們這種類型的對象上直接調用它。它只有通過一個衍生類調用;對那個衍生類來說,如果要保證它正常工作,需實現Cloneable。
(5) 不實現Cloneable來試著防止克隆,並覆蓋clone(),以產生一個違例。為使這一設想順利實現,只有令從它衍生出來的任何類都調用重新定義後的clone()裡的suepr.clone()。
(6) 將類設為final,從而防止克隆。若clone()尚未被我們的任何一個上級類覆蓋,這一設想便不會成功。若已被覆蓋,那麼再一次覆蓋它,並“擲”出一個CloneNotSupportedException(克隆不支持)違例。為擔保克隆被禁止,將類設為final是唯一的辦法。除此以外,一旦涉及保密對象或者遇到想對創建的對象數量進行控制的其他情況,應該將所有構建器都設為private,並提供一個或更多的特殊方法來創建對象。采用這種方式,這些方法就可以限制創建的對象數量以及它們的創建條件——一種特殊情況是第16章要介紹的singleton(獨子)方案。
下面這個例子總結了克隆的各種實現方法,然後在層次結構中將其“關閉”:
//: CheckCloneable.java // Checking to see if a handle can be cloned // Can't clone this because it doesn't // override clone(): class Ordinary {} // Overrides clone, but doesn't implement // Cloneable: class WrongClone extends Ordinary { public Object clone() throws CloneNotSupportedException { return super.clone(); // Throws exception } } // Does all the right things for cloning: class IsCloneable extends Ordinary implements Cloneable { public Object clone() throws CloneNotSupportedException { return super.clone(); } } // Turn off cloning by throwing the exception: class NoMore extends IsCloneable { public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } } class TryMore extends NoMore { public Object clone() throws CloneNotSupportedException { // Calls NoMore.clone(), throws exception: return super.clone(); } } class BackOn extends NoMore { private BackOn duplicate(BackOn b) { // Somehow make a copy of b // and return that copy. This is a dummy // copy, just to make the point: return new BackOn(); } public Object clone() { // Doesn't call NoMore.clone(): return duplicate(this); } } // Can't inherit from this, so can't override // the clone method like in BackOn: final class ReallyNoMore extends NoMore {} public class CheckCloneable { static Ordinary tryToClone(Ordinary ord) { String id = ord.getClass().getName(); Ordinary x = null; if(ord instanceof Cloneable) { try { System.out.println("Attempting " + id); x = (Ordinary)((IsCloneable)ord).clone(); System.out.println("Cloned " + id); } catch(CloneNotSupportedException e) { System.out.println( "Could not clone " + id); } } return x; } public static void main(String[] args) { // Upcasting: Ordinary[] ord = { new IsCloneable(), new WrongClone(), new NoMore(), new TryMore(), new BackOn(), new ReallyNoMore(), }; Ordinary x = new Ordinary(); // This won't compile, since clone() is // protected in Object: //! x = (Ordinary)x.clone(); // tryToClone() checks first to see if // a class implements Cloneable: for(int i = 0; i < ord.length; i++) tryToClone(ord[i]); } } ///:~
第一個類Ordinary代表著大家在本書各處最常見到的類:不支持克隆,但在它正式應用以後,卻也不禁止對其克隆。但假如有一個指向Ordinary對象的句柄,而且那個對象可能是從一個更深的衍生類上溯造型來的,便不能判斷它到底能不能克隆。
WrongClone類揭示了實現克隆的一種不正確途徑。它確實覆蓋了Object.clone(),並將那個方法設為public,但卻沒有實現Cloneable。所以一旦發出對super.clone()的調用(由於對Object.clone()的一個調用造成的),便會無情地擲出CloneNotSupportedException違例。
在IsCloneable中,大家看到的才是進行克隆的各種正確行動:先覆蓋clone(),並實現了Cloneable。但是,這個clone()方法以及本例的另外幾個方法並不捕獲CloneNotSupportedException違例,而是任由它通過,並傳遞給調用者。隨後,調用者必須用一個try-catch代碼塊把它包圍起來。在我們自己的clone()方法中,通常需要在clone()內部捕獲CloneNotSupportedException違例,而不是任由它通過。正如大家以後會理解的那樣,對這個例子來說,讓它通過是最正確的做法。
類NoMore試圖按照Java設計者打算的那樣“關閉”克隆:在衍生類clone()中,我們擲出CloneNotSupportedException違例。TryMore類中的clone()方法正確地調用super.clone(),並解析成NoMore.clone(),後者擲出一個違例並禁止克隆。
但在已被覆蓋的clone()方法中,假若程序員不遵守調用super.clone()的“正確”方法,又會出現什麼情況呢?在BackOn中,大家可看到實際會發生什麼。這個類用一個獨立的方法duplicate()制作當前對象的一個副本,並在clone()內部調用這個方法,而不是調用super.clone()。違例永遠不會產生,而且新類是可以克隆的。因此,我們不能依賴“擲”出一個違例的方法來防止產生一個可克隆的類。唯一安全的方法在ReallyNoMore中得到了演示,它設為final,所以不可繼承。這意味著假如clone()在final類中擲出了一個違例,便不能通過繼承來進行修改,並可有效地禁止克隆(不能從一個擁有任意繼承級數的類中明確調用Object.clone();只能調用super.clone(),它只可訪問直接基礎類)。因此,只要制作一些涉及安全問題的對象,就最好把那些類設為final。
在類CheckCloneable中,我們看到的第一個類是tryToClone(),它能接納任何Ordinary對象,並用instanceof檢查它是否能夠克隆。若答案是肯定的,就將對象造型成為一個IsCloneable,調用clone(),並將結果造型回Ordinary,最後捕獲有可能產生的任何違例。請注意用運行期類型鑒定(見第11章)打印出類名,使自己看到發生的一切情況。
在main()中,我們創建了不同類型的Ordinary對象,並在數組定義中上溯造型成為Ordinary。在這之後的頭兩行代碼創建了一個純粹的Ordinary對象,並試圖對其克隆。然而,這些代碼不會得到編譯,因為clone()是Object中的一個protected(受到保護的)方法。代碼剩余的部分將遍歷數組,並試著克隆每個對象,分別報告它們的成功或失敗。輸出如下:
Attempting IsCloneable Cloned IsCloneable Attempting NoMore Could not clone NoMore Attempting TryMore Could not clone TryMore Attempting BackOn Cloned BackOn Attempting ReallyNoMore Could not clone ReallyNoMore
總之,如果希望一個類能夠克隆,那麼:
(1) 實現Cloneable接口
(2) 覆蓋clone()
(3) 在自己的clone()中調用super.clone()
(4) 在自己的clone()中捕獲違例
這一系列步驟能達到最理想的效果。