The Cloneable interface was intended(目的) as a mixin interface (Item 20) for classes to advertise that they permit cloning. Unfortunately, it fails to serve this purpose. Its primary flaw(n. 瑕疵,缺點(diǎn)) is that it lacks a clone method, and Object’s clone method is protected. You cannot, without resorting(求助) to reflection (Item 65), invoke clone on an object merely(adv. 僅僅,只是) because it implements Cloneable.Even a reflective invocation may fail, because there is no guarantee(n. 保證;擔(dān)保) that the object has an accessible clone method. Despite this flaw and many others, the facility(n. 設(shè)施;設(shè)備) is in reasonably wide use, so it pays to understand it. This item tells you how to implement a well-behaved clone method, discusses when it is appropriate to do so, and presents alternatives.
Cloneable 接口的目的是作為 mixin 接口(Item-20),用于讓類來宣稱它們允許克隆。不幸的是,它沒有達(dá)到這個(gè)目的。它的主要缺點(diǎn)是缺少 clone 方法,并且 Object 類的 clone 方法是受保護(hù)的。如果不求助于反射(Item-65),就不能僅僅因?yàn)閷ο髮?shí)現(xiàn)了 Cloneable 就能調(diào)用 clone 方法。即使反射調(diào)用也可能失敗,因?yàn)椴荒鼙WC對象具有可訪問的 clone 方法。盡管存在這樣那樣的缺陷,但該設(shè)施的使用范圍相當(dāng)廣泛,因此理解它是值得的。本項(xiàng)目將告訴你如何實(shí)現(xiàn)行為良好的 clone 方法,討論什么時(shí)候應(yīng)該這樣做,并提供替代方法。
譯注:mixin 是摻合,混合,糅合的意思,即可以將任意一個(gè)對象的全部或部分屬性拷貝到另一個(gè)對象上。
So what does Cloneable do, given that it contains no methods? It determines the behavior of Object’s protected clone implementation: if a class implements Cloneable, Object’s clone method returns a field-byfield copy of the object; otherwise it throws CloneNotSupportedException. This is a highly atypical use of interfaces and not one to be emulated. Normally, implementing an interface says something about what a class can do for its clients. In this case, it modifies the behavior of a protected method on a superclass.
如果 Cloneable 不包含任何方法,它會(huì)做什么呢?它決定 Object 的受保護(hù)克隆實(shí)現(xiàn)的行為:如果一個(gè)類實(shí)現(xiàn)了 Cloneable,對象的克隆方法返回對象的逐域拷貝;否則它會(huì)拋出 CloneNotSupportedException。這是接口的一種高度非典型使用,而不是可模仿的。通常,實(shí)現(xiàn)接口說明了類可以為其客戶做些什么。在本例中,它修改了超類上受保護(hù)的方法行為。
Though the specification(n. 規(guī)格;說明書;詳述) doesn’t say it, in practice, a class implementing Cloneable is expected to provide a properly(adv. 適當(dāng)?shù)兀徽_地;恰當(dāng)?shù)兀?functioning public clone method. In order to achieve(vt. 取得;獲得;實(shí)現(xiàn);) this, the class and all of its superclasses must obey a complex(adj. 復(fù)雜的;合成的), unenforceable, thinly documented protocol. The resulting mechanism is fragile, dangerous, and extralinguistic(adj. 語言以外的;語言學(xué)以外的): it creates objects without calling a constructor.
雖然規(guī)范沒有說明,但是在實(shí)踐中,一個(gè)實(shí)現(xiàn) Cloneable 的類應(yīng)該提供一個(gè)功能正常的公共 clone 方法。為了實(shí)現(xiàn)這一點(diǎn),類及其所有超類必須遵守復(fù)雜的、不可強(qiáng)制執(zhí)行的、文檔很少的協(xié)議。產(chǎn)生的機(jī)制是脆弱的、危險(xiǎn)的和非語言的:即它創(chuàng)建對象而不調(diào)用構(gòu)造函數(shù)。
The general contract for the clone method is weak. Here it is, copied from the Object specification(n. 規(guī)格;說明書;詳述) :
clone 方法的一般約定很薄弱。這里是從 Object 規(guī)范復(fù)制過來的:
Creates and returns a copy of this object. The precise meaning of “copy” may depend on the class of the object. The general intent(n. 意圖;目的;含義,adj. 專心的;急切的;堅(jiān)決的) is that, for any object x,the expression
創(chuàng)建并返回此對象的副本。「復(fù)制」的確切含義可能取決于對象的類別。一般的目的是,對于任何對象 x,表達(dá)式
x.clone() != x
will be true, and the expression
值將為 true,并且這個(gè)表達(dá)式
x.clone().getClass() == x.getClass()
will be true, but these are not absolute requirements(n. 要求;必要條件;). While it is typically the case that
值將為 true,但這些不是絕對的必要條件。通常情況下
x.clone().equals(x)
will be true, this is not an absolute requirement.
值將為 true,但這些不是絕對的必要條件。
By convention(n. 大會(huì);慣例;約定;協(xié)定;習(xí)俗), the object returned by this method should be obtained(v. 獲得) by calling super.clone. If a class and all of its superclasses (except Object) obey this convention, it will be the case that
按照慣例,這個(gè)方法返回的對象應(yīng)該通過調(diào)用 super.clone 來獲得。如果一個(gè)類和它的所有超類(對象除外)都遵守這個(gè)約定,那么情況就是這樣
x.clone().getClass() == x.getClass().
By convention, the returned object should be independent of the object being cloned. To achieve this independence, it may be necessary to modify one or more fields of the object returned by super.clone before returning it.
按照慣例,返回的對象應(yīng)該獨(dú)立于被克隆的對象。為了實(shí)現(xiàn)這種獨(dú)立性,可能需要修改 super 返回的對象的一個(gè)或多個(gè)字段。在返回之前克隆。
This mechanism is vaguely similar to constructor chaining, except that it isn’t enforced: if a class’s clone method returns an instance that is not obtained by calling super.clone but by calling a constructor, the compiler won’t complain, but if a subclass of that class calls super.clone, the resulting object will have the wrong class, preventing the subclass from clone method from working properly. If a class that overrides clone is final, this convention may be safely ignored, as there are no subclasses to worry about. But if a final class has a clone method that does not invoke super.clone, there is no reason for the class to implement Cloneable, as it doesn’t rely on the behavior of Object’s clone implementation.
這種機(jī)制有點(diǎn)類似于構(gòu)造函數(shù)鏈接,只是沒有強(qiáng)制執(zhí)行:如果一個(gè)類的克隆方法返回的實(shí)例不是通過調(diào)用 super.clone 而是通過調(diào)用構(gòu)造函數(shù)獲得的,編譯器不會(huì)抱怨,但是如果這個(gè)類的一個(gè)子類調(diào)用 super.clone,由此產(chǎn)生的對象將有錯(cuò)誤的類,防止子類克隆方法從正常工作。如果覆蓋克隆的類是 final 的,那么可以安全地忽略這個(gè)約定,因?yàn)椴恍枰獡?dān)心子類。但是如果 final 類有一個(gè)不調(diào)用 super 的克隆方法。類沒有理由實(shí)現(xiàn) Cloneable,因?yàn)樗灰蕾囉趯ο罂寺?shí)現(xiàn)的行為。
Suppose you want to implement Cloneable in a class whose superclass provides a well-behaved clone method. First call super.clone. The object you get back will be a fully functional replica of the original. Any fields declared in your class will have values identical to those of the original. If every field contains a primitive value or a reference to an immutable object, the returned object may be exactly what you need, in which case no further processing is necessary. This is the case, for example, for the PhoneNumber class in Item 11, but note that immutable classes should never provide a clone method because it would merely encourage wasteful copying. With that caveat, here’s how a clone method for PhoneNumber would look:
假設(shè)你希望在一個(gè)類中實(shí)現(xiàn) Cloneable,該類的超類提供了一個(gè)表現(xiàn)良好的克隆方法。第一個(gè)叫 super.clone。返回的對象將是原始對象的完整功能副本。類中聲明的任何字段都具有與原始字段相同的值。如果每個(gè)字段都包含一個(gè)基元值或?qū)Σ豢勺儗ο蟮囊茫敲捶祷氐膶ο罂赡苷悄闼枰?,在這種情況下不需要進(jìn)一步的處理。例如,對于Item-11中的 PhoneNumber 類就是這樣,但是要注意,不可變類永遠(yuǎn)不應(yīng)該提供克隆方法,因?yàn)樗粫?huì)鼓勵(lì)浪費(fèi)復(fù)制。有了這個(gè)警告,以下是 PhoneNumber 的克隆方法的外觀:
// Clone method for class with no references to mutable state
@Override public PhoneNumber clone() {
try {
return (PhoneNumber) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // Can't happen
}
}
In order for this method to work, the class declaration for PhoneNumber would have to be modified to indicate that it implements Cloneable. Though Object’s clone method returns Object, this clone method returns PhoneNumber. It is legal and desirable to do this because Java supports covariant return types. In other words, an overriding method’s return type can be a subclass of the overridden method’s return type. This eliminates the need for casting in the client. We must cast the result of super.clone from Object to PhoneNumber before returning it, but the cast is guaranteed to succeed.
為了讓這個(gè)方法工作,必須修改 PhoneNumber 的類聲明,以表明它實(shí)現(xiàn)了 Cloneable。雖然 Object 的 clone 方法返回 Object,但是這個(gè) clone 方法返回 PhoneNumber。這樣做是合法的,也是可取的,因?yàn)?Java 支持協(xié)變返回類型。換句話說,覆蓋方法的返回類型可以是被覆蓋方法的返回類型的子類。這樣就不需要在客戶端中進(jìn)行強(qiáng)制轉(zhuǎn)換。我們必須打出超級的成績。在返回對象之前從對象克隆到 PhoneNumber,但強(qiáng)制轉(zhuǎn)換肯定會(huì)成功。
The call to super.clone is contained in a try-catch block. This is because Object declares its clone method to throw CloneNotSupportedException, which is a checked exception. Because PhoneNumber implements Cloneable, we know the call to super.clone will succeed. The need for this boilerplate indicates that CloneNotSupportedException should have been unchecked (Item 71).
對 super.clone 的調(diào)用包含在 try-catch 塊中。這是因?yàn)?Object 聲明其克隆方法來拋出 CloneNotSupportedException。因?yàn)?PhoneNumber 實(shí)現(xiàn)了 Cloneable,所以我們知道對 super.clone 的調(diào)用將會(huì)成功。這個(gè)樣板文件的需要表明 CloneNotSupportedException 應(yīng)該是被選中的(Item-71)。
If an object contains fields that refer to mutable objects, the simple clone implementation shown earlier can be disastrous. For example, consider the Stack class in Item 7:
如果對象包含引用可變對象的字段,前面所示的簡單克隆實(shí)現(xiàn)可能是災(zāi)難性的。例如,考慮 Item-7 中的堆棧類:
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
// Ensure space for at least one more element.
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
Suppose you want to make this class cloneable. If the clone method merely(adv. 僅僅,只不過;只是) returns super.clone(), the resulting Stack instance will have the correct value in its size field, but its elements field will refer to the same array as the original Stack instance. Modifying the original will destroy the invariants in the clone and vice versa. You will quickly find that your program produces nonsensical results or throws a NullPointerException.
假設(shè)你想讓這個(gè)類是可克隆的。如果克隆方法只返回 super.clone(),則結(jié)果堆棧實(shí)例在其大小字段中將有正確的值,但其元素字段將引用與原始堆棧實(shí)例相同的數(shù)組。修改初始值將破壞克隆中的不變量,反之亦然。你將很快發(fā)現(xiàn)你的程序產(chǎn)生了無意義的結(jié)果或拋出 NullPointerException。
This situation could never occur as a result of calling the sole constructor in the Stack class. In effect, the clone method functions as a constructor;you must ensure that it does no harm to the original object and that it properly establishes invariants on the clone. In order for the clone method on Stack to work properly, it must copy the internals of the stack. The easiest way to do this is to call clone recursively on the elements array:
由于調(diào)用堆棧類中的唯一構(gòu)造函數(shù),這種情況永遠(yuǎn)不會(huì)發(fā)生。實(shí)際上,clone 方法充當(dāng)構(gòu)造函數(shù);你必須確保它不會(huì)對原始對象造成傷害,并且在克隆上正確地建立不變量。為了使堆棧上的克隆方法正常工作,它必須復(fù)制堆棧的內(nèi)部。最簡單的方法是在元素?cái)?shù)組上遞歸地調(diào)用 clone:
// Clone method for class with references to mutable state
@Override
public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
Note that we do not have to cast the result of elements.clone to Object[]. Calling clone on an array returns an array whose runtime and compile-time types are identical to those of the array being cloned. This is the preferred idiom to duplicate an array. In fact, arrays are the sole compelling use of the clone facility(n. 設(shè)施;設(shè)備;容易;靈巧).
注意,我們不需要將 elements.clone 的結(jié)果強(qiáng)制轉(zhuǎn)換到 Object[]。在數(shù)組上調(diào)用 clone 將返回一個(gè)數(shù)組,該數(shù)組的運(yùn)行時(shí)和編譯時(shí)類型與被克隆的數(shù)組相同。這是復(fù)制數(shù)組的首選習(xí)慣用法。實(shí)際上,數(shù)組是 clone 工具唯一引人注目的用途。
Note also that the earlier solution would not work if the elements field were final because clone would be prohibited from assigning a new value to the field. This is a fundamental problem: like serialization, the Cloneable architecture is incompatible with normal use of final fields referring to mutable objects, except in cases where the mutable objects may be safely shared between an object and its clone. In order to make a class cloneable, it may be necessary to remove final modifiers from some fields.
還要注意,如果元素字段是 final 的,早期的解決方案就無法工作,因?yàn)榭寺⒈唤篂樽侄畏峙湫轮?。這是一個(gè)基本問題:與序列化一樣,可克隆體系結(jié)構(gòu)與正常使用引用可變對象的 final 字段不兼容,除非在對象與其克隆對象之間可以安全地共享可變對象。為了使類可克隆,可能需要從某些字段中刪除最終修飾符。
It is not always sufficient(adj. 足夠的;充分的) merely to call clone recursively. For example,suppose you are writing a clone method for a hash table whose internals consist of an array of buckets, each of which references the first entry in a linked list of key-value pairs. For performance, the class implements its own lightweight singly linked list instead of using java.util.LinkedList internally:
僅僅遞歸地調(diào)用克隆并不總是足夠的。例如,假設(shè)你正在為 hash 表編寫一個(gè)克隆方法, hash 表的內(nèi)部由一組 bucket 組成,每個(gè) bucket 引用鍵-值對鏈表中的第一個(gè)條目。為了提高性能,類實(shí)現(xiàn)了自己的輕量級單鏈表,而不是在內(nèi)部使用 java.util.LinkedList:
public class HashTable implements Cloneable {
private Entry[] buckets = ...;
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
} ... // Remainder omitted
}
Suppose you merely clone the bucket array recursively, as we did for Stack:
假設(shè)你只是遞歸地克隆 bucket 數(shù)組,就像我們對 Stack 所做的那樣:
// Broken clone method - results in shared mutable state!
@Override
public HashTable clone() {
try {
HashTable result = (HashTable) super.clone();
result.buckets = buckets.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
Though the clone has its own bucket array, this array references the same linked lists as the original, which can easily cause nondeterministic behavior in both the clone and the original. To fix this problem, you’ll have to copy the linked list that comprises each bucket. Here is one common approach:
盡管克隆具有自己的 bucket 數(shù)組,但該數(shù)組引用的鏈接列表與原始鏈表相同,這很容易導(dǎo)致克隆和原始的不確定性行為。要解決這個(gè)問題,你必須復(fù)制包含每個(gè) bucket 的鏈表。這里有一個(gè)常見的方法:
// Recursive clone method for class with complex mutable state
public class HashTable implements Cloneable {
private Entry[] buckets = ...;
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
// Recursively copy the linked list headed by this Entry
Entry deepCopy() {
return new Entry(key, value,next == null ? null : next.deepCopy());
}
}
@Override
public HashTable clone() {
try {
HashTable result = (HashTable) super.clone();
result.buckets = new Entry[buckets.length];
for (int i = 0; i < buckets.length; i++)
if (buckets[i] != null)
result.buckets[i] = buckets[i].deepCopy();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
} ... // Remainder omitted
}
The private class HashTable.Entry has been augmented to support a “deep copy” method. The clone method on HashTable allocates a new buckets array of the proper size and iterates over the original buckets array,deep-copying each nonempty bucket. The deepCopy method on Entry invokes itself recursively to copy the entire linked list headed by the entry. While this technique is cute and works fine if the buckets aren’t too long, it is not a good way to clone a linked list because it consumes one stack frame for each element in the list. If the list is long, this could easily cause a stack overflow. To prevent this from happening, you can replace the recursion in deepCopy with iteration:
私有類 HashTable.Entry 已經(jīng)被增強(qiáng)為支持「深度復(fù)制」方法。HashTable 上的 clone 方法分配一個(gè)大小合適的新 bucket 數(shù)組,并遍歷原始 bucket 數(shù)組,深度復(fù)制每個(gè)非空 bucket。條目上的 deepCopy 方法會(huì)遞歸地調(diào)用自己來復(fù)制以條目開頭的整個(gè)鏈表。雖然這種技術(shù)很可愛,而且如果 bucket 不太長也可以很好地工作,但是克隆鏈表并不是一個(gè)好方法,因?yàn)樗鼮殒湵碇械拿總€(gè)元素消耗一個(gè)堆棧幀。如果列表很長,很容易導(dǎo)致堆棧溢出。為了防止這種情況的發(fā)生,你可以用迭代替換 deepCopy 中的遞歸:
// Iteratively copy the linked list headed by this Entry
Entry deepCopy() {
Entry result = new Entry(key, value, next);
for (Entry p = result; p.next != null; p = p.next)
p.next = new Entry(p.next.key, p.next.value, p.next.next);
return result;
}
A final approach to cloning complex mutable objects is to call super.clone, set all of the fields in the resulting object to their initial state,and then call higher-level methods to regenerate the state of the original object.In the case of our HashTable example, the buckets field would be initialized to a new bucket array, and the put(key, value) method (not shown) would be invoked for each key-value mapping in the hash table being cloned. This approach typically yields a simple, reasonably elegant clone method that does not run as quickly as one that directly manipulates the innards of the clone. While this approach is clean, it is antithetical to the whole Cloneable architecture because it blindly overwrites the field-by-field object copy that forms the basis of the architecture.
克隆復(fù)雜可變對象的最后一種方法是調(diào)用 super.clone,將結(jié)果對象中的所有字段設(shè)置為初始狀態(tài),然后調(diào)用更高級別的方法重新生成原始對象的狀態(tài)。在我們的 HashTable 示例中,bucket 字段將初始化為一個(gè)新的 bucket 數(shù)組,并且對于克隆的 hash 表中的每個(gè)鍵值映射將調(diào)用 put(key, value)方法(未顯示)。這種方法通常產(chǎn)生一個(gè)簡單、相當(dāng)優(yōu)雅的克隆方法,它的運(yùn)行速度不如直接操作克隆的內(nèi)部的方法快。雖然這種方法很簡潔,但它與整個(gè)可克隆體系結(jié)構(gòu)是對立的,因?yàn)樗つ康馗采w了構(gòu)成體系結(jié)構(gòu)基礎(chǔ)的逐字段對象副本。
Like a constructor, a clone method must never invoke an overridable method on the clone under construction (Item 19). If clone invokes a method that is overridden in a subclass, this method will execute before the subclass has had a chance to fix its state in the clone, quite possibly leading to corruption in the clone and the original. Therefore, the put(key, value) method discussed in the previous paragraph should be either final or private. (If it is private, it is presumably the “helper method” for a nonfinal public method.)
與構(gòu)造函數(shù)一樣,克隆方法決不能在正在構(gòu)建的克隆上調(diào)用可覆蓋方法(Item-19)。如果 clone 調(diào)用一個(gè)在子類中被重寫的方法,這個(gè)方法將在子類有機(jī)會(huì)修復(fù)其在克隆中的狀態(tài)之前執(zhí)行,很可能導(dǎo)致克隆和原始的破壞。因此,前一段中討論的 put(key, value)方法應(yīng)該是 final 或 private 方法。(如果它是私有的,那么它可能是非最終公共方法的「助手方法」。)
Object’s clone method is declared to throw CloneNotSupportedException, but overriding methods need not. Public clone methods should omit the throws clause, as methods that don’t throw checked exceptions are easier to use (Item 71).
對象的 clone 方法被聲明為拋出 CloneNotSupportedException,但是重寫方法不需要。公共克隆方法應(yīng)該省略 throw 子句, 作為不拋出受控異常的方法更容易使用(Item-71)。
You have two choices when designing a class for inheritance (Item 19), but whichever one you choose, the class should not implement Cloneable. You may choose to mimic the behavior of Object by implementing a properly functioning protected clone method that is declared to throw CloneNotSupportedException. This gives subclasses the freedom to implement Cloneable or not, just as if they extended Object directly.Alternatively, you may choose not to implement a working clone method, and to prevent subclasses from implementing one, by providing the following degenerate clone implementation:
在為繼承設(shè)計(jì)類時(shí),你有兩種選擇(Item-19),但是無論你選擇哪一種,類都不應(yīng)該實(shí)現(xiàn) Cloneable。你可以選擇通過實(shí)現(xiàn)一個(gè)功能正常的受保護(hù)克隆方法來模擬對象的行為,該方法聲明為拋出 CloneNotSupportedException。這給子類實(shí)現(xiàn) Cloneable 或不實(shí)現(xiàn) Cloneable 的自由,就像它們直接擴(kuò)展對象一樣?;蛘?,你可以選擇不實(shí)現(xiàn)工作克隆方法,并通過提供以下簡并克隆實(shí)現(xiàn)來防止子類實(shí)現(xiàn)一個(gè)工作克隆方法:
// clone method for extendable class not supporting Cloneable
@Override
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
There is one more detail that bears noting. If you write a thread-safe class that implements Cloneable, remember that its clone method must be properly synchronized, just like any other method (Item 78). Object’s clone method is not synchronized, so even if its implementation is otherwise satisfactory, you may have to write a synchronized clone method that returns super.clone().
還有一個(gè)細(xì)節(jié)需要注意。如果你編寫了一個(gè)實(shí)現(xiàn)了 Cloneable 的線程安全類,請記住它的克隆方法必須正確同步,就像其他任何方法一樣(Item-78)。對象的克隆方法不是同步的,因此即使它的實(shí)現(xiàn)在其他方面是令人滿意的,你也可能需要編寫一個(gè)返回 super.clone()的同步克隆方法。
To recap, all classes that implement Cloneable should override clone with a public method whose return type is the class itself. This method should first call super.clone, then fix any fields that need fixing. Typically, this means copying any mutable objects that comprise the internal “deep structure” of the object and replacing the clone’s references to these objects with references to their copies. While these internal copies can usually be made by calling clone recursively, this is not always the best approach. If the class contains only primitive fields or references to immutable objects, then it is likely the case that no fields need to be fixed. There are exceptions to this rule. For example, a field representing a serial number or other unique ID will need to be fixed even if it is primitive or immutable.
回顧一下,所有實(shí)現(xiàn) Cloneable 的類都應(yīng)該使用一個(gè)返回類型為類本身的公共方法覆蓋 clone。這個(gè)方法應(yīng)該首先調(diào)用 super.clone,然后修復(fù)任何需要修復(fù)的字段。通常,這意味著復(fù)制任何包含對象內(nèi)部「深層結(jié)構(gòu)」的可變對象,并將克隆對象對這些對象的引用替換為對其副本的引用。雖然這些內(nèi)部副本通??梢酝ㄟ^遞歸調(diào)用 clone 來實(shí)現(xiàn),但這并不總是最好的方法。如果類只包含基元字段或?qū)Σ豢勺儗ο蟮囊茫敲春芸赡懿恍枰迯?fù)任何字段。這條規(guī)則也有例外。例如,表示序列號(hào)或其他唯一 ID 的字段需要固定,即使它是原始的或不可變的。
Is all this complexity really necessary? Rarely. If you extend a class that already implements Cloneable, you have little choice but to implement a well-behaved clone method. Otherwise, you are usually better off providing an alternative means of object copying. A better approach to object copying is to provide a copy constructor or copy factory. A copy constructor is simply a constructor that takes a single argument whose type is the class containing the constructor, for example,
所有這些復(fù)雜性真的有必要嗎?很少。如果你擴(kuò)展了一個(gè)已經(jīng)實(shí)現(xiàn)了 Cloneable 的類,那么除了實(shí)現(xiàn)行為良好的克隆方法之外,你別無選擇。否則,最好提供對象復(fù)制的替代方法。一個(gè)更好的對象復(fù)制方法是提供一個(gè)復(fù)制構(gòu)造函數(shù)或復(fù)制工廠。復(fù)制構(gòu)造函數(shù)是一個(gè)簡單的構(gòu)造函數(shù),它接受單個(gè)參數(shù),其類型是包含構(gòu)造函數(shù)的類,例如,
// Copy constructor
public Yum(Yum yum) { ... };
A copy factory is the static factory (Item 1) analogue of a copy constructor:
復(fù)制工廠是復(fù)制構(gòu)造函數(shù)的靜態(tài)工廠(Item-1)類似物:
// Copy factory
public static Yum newInstance(Yum yum) { ... };
The copy constructor approach and its static factory variant have many advantages over Cloneable/clone: they don’t rely on a risk-prone extralinguistic object creation mechanism; they don’t demand unenforceable adherence to thinly documented conventions; they don’t conflict with the proper use of final fields; they don’t throw unnecessary checked exceptions; and they don’t require casts.
復(fù)制構(gòu)造函數(shù)方法及其靜態(tài)工廠變體與克隆/克隆相比有許多優(yōu)點(diǎn):它們不依賴于易發(fā)生風(fēng)險(xiǎn)的語言外對象創(chuàng)建機(jī)制;他們不要求無法強(qiáng)制執(zhí)行的約定;它們與最終字段的正確使用不沖突;它們不會(huì)拋出不必要的檢查異常;而且不需要強(qiáng)制類型轉(zhuǎn)換。
Furthermore, a copy constructor or factory can take an argument whose type is an interface implemented by the class. For example, by convention all generalpurpose collection implementations provide a constructor whose argument is of type Collection or Map. Interface-based copy constructors and factories,more properly known as conversion constructors and conversion factories, allow the client to choose the implementation type of the copy rather than forcing the client to accept the implementation type of the original. For example, suppose you have a HashSet, s, and you want to copy it as a TreeSet. The clone method can’t offer this functionality, but it’s easy with a conversion constructor:new TreeSet<>(s).
此外,復(fù)制構(gòu)造函數(shù)或工廠可以接受類型為類實(shí)現(xiàn)的接口的參數(shù)。例如,按照約定,所有通用集合實(shí)現(xiàn)都提供一個(gè)構(gòu)造函數(shù),其參數(shù)為 collection 或 Map 類型。基于接口的復(fù)制構(gòu)造函數(shù)和工廠(更確切地稱為轉(zhuǎn)換構(gòu)造函數(shù)和轉(zhuǎn)換工廠)允許客戶端選擇副本的實(shí)現(xiàn)類型,而不是強(qiáng)迫客戶端接受原始的實(shí)現(xiàn)類型。例如,假設(shè)你有一個(gè) HashSet s,并且希望將它復(fù)制為 TreeSet。克隆方法不能提供這種功能,但是使用轉(zhuǎn)換構(gòu)造函數(shù)很容易:new TreeSet<>(s)。
Given all the problems associated(adj. 關(guān)聯(lián)的;聯(lián)合的) with Cloneable, new interfaces should not extend it, and new extendable classes should not implement it. While it’s less harmful for final classes to implement Cloneable, this should be viewed as a performance optimization, reserved for the rare cases where it is justified (Item 67). As a rule, copy functionality is best provided by constructors or factories. A notable exception to this rule is arrays, which are best copied with the clone method.
考慮到與 Cloneable 相關(guān)的所有問題,新的接口不應(yīng)該擴(kuò)展它,新的可擴(kuò)展類不應(yīng)該實(shí)現(xiàn)它。雖然 final 類實(shí)現(xiàn) Cloneable 的危害要小一些,但這應(yīng)該被視為一種性能優(yōu)化,僅在極少數(shù)情況下(Item-67)是合理的。通常,復(fù)制功能最好由構(gòu)造函數(shù)或工廠提供。這個(gè)規(guī)則的一個(gè)明顯的例外是數(shù)組,最好使用 clone 方法來復(fù)制數(shù)組。
Back to contents of the chapter(返回章節(jié)目錄)
- Previous Item(上一條目):Item 12: Always override toString(始終覆蓋 toString 方法)
- Next Item(下一條目):Item 14: Consider implementing Comparable(考慮實(shí)現(xiàn) Comparable 接口)