簡體   English   中英

覆蓋 Clone 而不調用 super.clone

[英]Overriding Clone without calling the super.clone

在不實現 Cloneable 接口和不調用 super.clone() 的情況下,覆蓋clone方法是否是一個好習慣。 這樣就不會拋出CloneNotSupportedException異常。

考慮這個類:

class Money {

    private BigDecimal x ;

    public Object clone() {
        Money m1 = new Money();
        m1.setX(this.x);
        return m1;
    }

    public BigDecimal getX() {
        return x;
    }

    public void setX(BigDecimal x) {
        this.x = x;
    }

}        

這個類不會拋出CloneNotSupportedException並且它的工作方式就像一個復制構造函數。

這是一個好方法嗎?

不調用super.clone()是不好的做法。 有關如何實現克隆方法的更多信息,請參閱此答案

您有克隆邏輯,當然您知道,您的類支持克隆,因此clone方法不應拋出CloneNotSupportedException 因此,在此處調用super.clone()會導致您編寫樣板try / catch塊,並重新拋出包裹在AssertionError CloneNotSupportedException ,這顯然從未被拋出。 而這個標記Cloneable ......我認為,Java 的這一部分設計不當。 所以我只是忽略文檔並手動復制字段。

使用super.clone()的唯一兩個參數是性能(我想,類似於內部使用的memcpy )和添加新字段時的錯誤持久性。

在你的情況下,它是可行的,但在某些情況下,它甚至不起作用。 在不調用super.clone情況下實現clone方法將無法使子類 cloneable 例如,假設我有一個Fruit

public class Fruit implements Cloneable {

    private String fruitName;

    public Fruit(String fruitName) {
        this.fruitName = fruitName;
    }

    public String getFruitName() {
        return fruitName;
    }

    public Fruit clone() {
        return new Fruit(fruitName);
    }
}

正如你所看到的,我在沒有調用super.clone情況下實現Fruit#clone方法,這看起來很好。 但是現在我想創建一個擴展Fruit的類Apple ,如下所示:

public class Apple extends Fruit {

    private String origin;

    public Apple(String fruitName, String origin) {
        super(fruitName);
        this.origin = origin;
    }

    public Apple clone() {
        // TODO
    }

}

現在的問題是,如何實現蘋果的clone方法。 我應該在 Apple 的clone方法中調用super.clone嗎? 是的,您應該這樣做,否則您將無法獲得fruitName的值,該值是Fruit的私有字段。 好的,讓我們試試這個:

public Apple clone() {
    return (Apple) super.clone();
}

然而,它沒有用。 因為Fruit#clone不調用super.clone ,所以在Apple#clonesuper.clone()的結果返回一個Fruit的實例而不是Apple Fruit不能被扔進Apple ,因此會拋出一個錯誤:

Exception in thread "main" java.lang.ClassCastException: Fruit cannot be cast to Apple
    at Apple.clone(Apple.java:20)

如您所見,您無法為Apple提供可行的clone方法,無論您是否調用super.clone ,它都不起作用。 只是因為你沒有在Fruit#clone調用super.clone

總之,如果您希望子類可clone ,則需要在clone方法中調用super.clone

在您的情況下,您有一個具有clone方法的類,而無需告訴外界它實際上是Cloneable 這不稱為克隆,而是原型制作。

如果要clone使用該接口,否則請選擇其他方法名稱。

還請檢查文檔:

創建並返回此對象的副本。 “復制”的確切含義可能取決於對象的類別。 一般意圖是,對於任何對象 x,表達式:x.clone() != x 將為真,並且表達式:x.clone().getClass() == x.getClass() 將為真,但這些都不是絕對的要求。 雖然通常的情況是: x.clone().equals(x) 為真,但這不是絕對要求。 按照慣例,返回的對象應該通過調用 super.clone 來獲取。 如果一個類和它的所有超類(Object 除外)都遵守這個約定,那么 x.clone().getClass() == x.getClass() 就是這種情況。

您必須調用super.clone()是有原因的。

clone 是關於處理/復制對象的類型和狀態。 如果我們必須確定類型,並且將它與 Object 類相關聯是有意義的。 Java 本質上是通過引用和淺拷貝原理進行操作的。 如果我們必須啟用深度復制,那么用戶必須在 clone() 中指明運行時特殊處理已完成。 這是通過使用 Clonable 接口進行標記來完成的。

理論上,添加對 clone 的覆蓋是強制執行原型模式的正確方法,即在不知道類型的情況下在運行時創建對象的副本。

實際上它是一個不同的故事。 克隆是執行此操作的 API 標准方法。 當您為獨立類編寫自己的克隆方法時,可以放棄 super.clone 調用並繼續。 但這是一種幼稚的做法。 假設您有一個遵循繼承機制的類。 想象一下,獅子是食肉動物,因此又是動物。 現在,如果您將 super.clone 放在 Lion 中,您最終可能會重新創建 Lion 的副本,而它既不是食肉動物,也不是上帝禁止的動物。

因此,最好遵循 API 標准並避免出現任何問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM