2012-07-18 8 views
18

Weź pod uwagę następujące kwestie od Effective Java Item 11 (Zastępuj klon rozsądnie), gdzie Josh Bloch wyjaśnia, co jest nie tak z umową clone().Efektywna Java: Analiza metody clone()

Istnieje wiele problemów z tą umową. Przepis, że konstruktorzy "no nazywają się" jest zbyt silny. Dobrze zachowana metoda klonowania może wywoływać konstruktory , aby tworzyć obiekty wewnętrzne dla budowanego klonu. Jeśli klasa jest końcowa, klon może nawet zwrócić obiekt utworzony przez konstruktor.

Może ktoś wyjaśnić, co Josh Bloch mówi w akapicie pierwszym, przez „Jeśli klasa jest final, clone może nawet zwrócić obiekt utworzony przez konstruktora.” Co tutaj ma do czynienia z final z clone()?

Odpowiedz

19

Jeśli klasa nie jest ostateczna, clone musi zwrócić najbardziej pochodną klasę, dla której została wywołana. To nie może działać z konstruktorem, ponieważ clone nie wie, do którego należy zadzwonić. Jeśli klasa jest ostateczna, nie może mieć żadnych podklas, więc nie ma niebezpieczeństwa w wywołaniu jej konstruktora podczas klonowania.

2

Umowa na clone określa, że ​​"Zgodnie z konwencją zwracany obiekt należy uzyskać, dzwoniąc pod numer super.clone". Jeśli twoja klasa nie jest ostateczna i zwrócisz coś otrzymanego za pomocą wywołania konstruktora, wywołanie super.clone() z podklasy nie zwróci oczekiwanego wyniku (po pierwsze, typ zwracanego obiektu nie będzie typem podklasy, ponieważ natywny clone() metoda powrócił).

6

Klasa nie musi zapewniać własnej implementacji clone, aby być klonowalnym. Może przekazać to do swojej klonowalnej superklasy. Nadchodzi haczyk: clone musi zawsze zwracać instancję tej samej klasy co instancja, do której jest wywoływana. Nie można tego osiągnąć w opisywanym przypadku, jeśli wywoływany jest jawny konstruktor. Jeśli klasa przejdzie na ostatnią, to z drugiej strony będzie w porządku.

21

To dlatego, że typowe implementacje clone() wygląda następująco:

public class MyClass implements Cloneable { 
    protected Object clone() { 
    MyClass cloned = (MyClass) super.clone(); 
    // set additional clone properties here 
    } 
} 

W ten sposób można dziedziczyć zachowanie klonowania ze swojej nadrzędnej. Jest szeroko założono, że wynik operacji clone() zwróci poprawny typ instancji na podstawie obiektu, do którego został wywołany. To znaczy. this.getClass()

Jeśli więc klasa jest ostateczna, nie musisz się martwić o podklasę wywołującą super.clone() i nie odzyskiwanie odpowiedniego typu obiektu.

public class A implements Cloneable { 
    public Object clone() { 
     return new A(); 
    } 
} 


public class B extends A { 
    public Object clone() { 
     B b = (B)super.clone(); // <== will throw ClassCastException 
    } 
} 

Ale jeśli A jest ostateczne, nikt nie może go przedłużyć, a zatem można bezpiecznie używać konstruktora.