2009-11-26 185 views
6

改變這個問題已經太晚了,但更確切地說,會問「爲什麼clone()不允許singleton?」。 A copy()方法會更方便。爲什麼Java枚舉不可克隆?


爲什麼Java中的枚舉不能被克隆?

手冊表明

這保證永遠不會複製枚舉,這是必要的維護自己的「單身」狀態。

但是,返回實例本身也會保留其狀態,我可以像處理其他可克隆對象一樣處理關聯的枚舉。

可能有人會認爲

總的意圖[克隆的()]是,對於任何對象x,表達式: x.clone() != x將爲真,[...]

但是對於單身人士而言,我希望x.clone() == x爲真。如果實例本身將被返回,那麼singleton模式對於引用對象是透明的。

那麼爲什麼enums不允許被克隆,或者當clone()被指定時,他們忘記考慮單身人士和不可變的東西?

+0

使用枚舉,有什麼可克隆的? – omerkudat 2009-11-26 13:05:43

回答

5

但恰恰相反單身我想x.clone() == x是真的。

可能想,但我認爲這是奇怪的是,下面的代碼將打破:

interface Foo extends Cloneable { public int getX(); public void setX(int x); } 
enum FooSingleton implements Foo { 
    INSTANCE; 
    private int x; 
    public int getX(){ return x; } 
    public void setX(int x){ this.x = x; } 
} 
class FooClass implements Foo { 
    private int x; 
    public int getX(){ return x; } 
    public void setX(int x){ this.x = x; } 
} 

boolean omg(Foo f){ 
    Foo c = f.clone(); 
    c.setX(c.getX() + 1); 
    return c.getX() != f.getX(); 
} 
assert omg(new FooClass());  // OK 
assert omg(FooSingleton.INSTANCE); // WTF? 

(當然,由於clone()只給出淺拷貝,甚至是正確執行的它可能導致在上面的代碼中出現錯誤。)

另一方面,我可以認爲克隆操作對於不可變對象只有return this有意義,並且枚舉實際上應該是不可變的。現在,當編寫clone()的合同時,他們顯然沒有考慮不可變性,或者他們不想爲一種不受語言支持的概念(即不可變類型)的概念提出特殊情況。

所以,clone()就是這樣,你不能很好地去改變自Java 1.0以來一直存在的東西。我很肯定在那裏的某個地方,有一些代碼完全依賴於clone()返回一個新的不同的對象,可能是作爲IdentityHashMap或某物的關鍵字。

1

我想他們不想把singletons當作clone()指定的特例。這會使規範複雜化。所以現在圖書館開發者必須把它們當作一種特殊情況,但是對於我們其他人來說,我們可以信任x.clone() != x是很好的。

6

如果你的克隆方法返回this實例而不是一個獨特的對象,那麼它不是一個克隆,是嗎?

The Javadoc說:

按照慣例,由 返回的對象這種方法應該是獨立的 這個對象(其被克隆)。

枚舉不應該被克隆,因爲應該只有每個值的一個實例。

編輯:針對以下注釋:

這正是我批評。爲什麼 不能返回相同的實例,如果有 不能是一個不同的實例?

因爲它沒有意義。如果它是相同的對象,那麼它不是一個克隆。Javadoc中也說:

總的意圖是,對於任何 對象x,表達式:

x.clone() != x
將是真實的,那 表達:
x.clone().getClass() == x.getClass()
將是真實的,但這些 不是絕對的要求。

所以意圖是爲clone()方法返回一個不同的對象。不幸的是,它並不是絕對的要求,這使得您的建議有效,但我仍然認爲這不合理,因爲使用返回this的克隆方法沒有用。如果你在你的枚舉常量中做了一些可疑的狀態,或者對它們進行同步,那麼它甚至可能會導致問題。這種代碼的行爲會根據克隆方法是否進行適當的克隆或僅返回this而有所不同。

你不能真正解釋爲什麼你想把枚舉看作Cloneable,因爲它們本質上是不可複製的。想要擁有一種不服從公認慣例的克隆方法,似乎是解決一些更爲根本性問題的方法。

+0

由於「克隆」方法的「合同」是要返回一份副本。 – 2009-11-26 14:16:48

+1

@Stephen C:是的,但並不是每一個對象都必須遵守那個合同,也就是說,並不是每個類都應該實現Cloneable。 – 2009-11-26 15:27:13

+0

我認爲你所引用的Javadoc實際上支持OPs的觀點:所有Java程序員中有99%會考慮對象狀態的獨立性,即修改'x'的狀態不會影響(結果的)狀態前一個)'x.clone()'。在這個意義上,通過return this來實現'clone()'是非常合理的。我甚至會說,任何依賴於clone()結果的非身份的代碼比通過** immutable ** ** singleton **實例到第三方代碼的代碼更有可能被破壞。 )方法,試圖克隆()',例如存儲對象狀態。 – misberner 2013-09-14 08:13:56

8

克隆單身人士的目的是什麼,如果x.clone() == x?你不能直接使用x

嚴格地說,如果你想克隆的東西執行x.clone() == x,可以是克隆的結果的唯一對象是x本身:

def clone() { 
    return this; 
} 

這可能會產生誤導...


如果你設計的東西,並基於clone()區分,你做錯了恕我直言......

+0

@Christian,你的代碼是否與其他不實現Cloneable的對象打交道,還是一切都必須是可複製的? – 2009-11-26 14:27:53

1

你自己的答案是最好的。一般而言,人們期望clone()能夠回饋不同的對象。 Cloneable本身的語義更具有這種意義。 (「該對象是可複製的......哦,我必須能夠複製。」)我不能想到的情況下,重要的是,但這是Cloneable的意圖語義。

我認爲,即使他們在考慮單身人士,他們也不會改變它。畢竟,程序員有責任通過有選擇地添加(並可能覆蓋)接口來決定可以克隆什麼和不能做什麼,而大多數程序員也不會將接口添加到單例中。

+0

對於不可變的對象,從字面上來說,與正確實現'clone()'和正確返回這個''唯一的區別是'x.clone()!= x'的結果,你沒有理由關心它。如果您處於可能有可變或不可變對象的情況,只需製作一個實際副本。不可變對象的副本很便宜 - 您不必遞減對象圖,只需將字段分配爲相同即可。 – Kevin 2015-08-10 04:56:25

0

但是對於單身人士而言,我希望x.clone() == x爲真。

不,這不會是一個克隆。因此,對於單身,你想這樣:

public Object clone() throws CloneNotSupportedException { 
    throw new CloneNotSupportedException(); 
} 
+1

是的,你應該。克隆**應該**返回一個對單身人士沒有意義的克隆。 – 2009-11-26 14:01:24

+0

你爲什麼要把clone作爲公共的? – 2009-11-26 22:04:16

+1

也許因爲這是重寫'clone()'時的約定,請參閱http://java.sun.com/javase/6/docs/api/java/lang/Cloneable.html。 – 2009-11-26 22:22:38