Dlaczego metoda clone () jest chroniona w java.lang.Object?


Odpowiedzi:


107

Fakt, że klon jest chroniony, jest niezwykle wątpliwy - podobnie jak fakt, że clonemetoda nie jest zadeklarowana w Cloneableinterfejsie.

To sprawia, że ​​metoda jest całkiem bezużyteczna do kopiowania danych, ponieważ nie można powiedzieć :

if(a instanceof Cloneable) {
    copy = ((Cloneable) a).clone();
}

Myślę, że projekt Cloneablejest obecnie w dużej mierze uważany za błąd (cytat poniżej). Normalnie chciałbym mieć możliwość tworzenia implementacji interfejsu, Cloneableale niekoniecznie tworzenia interfejsuCloneable (podobnie jak w przypadku Serializable). Nie można tego zrobić bez refleksji:

ISomething i = ...
if (i instanceof Cloneable) {
   //DAMN! I Need to know about ISomethingImpl! Unless...
   copy = (ISomething) i.getClass().getMethod("clone").invoke(i);
}

Cytat z Effective Java Josha Blocha :
„Interfejs Cloneable został pomyślany jako interfejs mieszany dla obiektów w celu ogłaszania, że ​​zezwalają na klonowanie. Niestety nie spełnia on tego celu ... Jest to wysoce nietypowe użycie interfejsów i nie należy go emulować ... Aby zaimplementowanie interfejsu miało jakikolwiek wpływ na klasę, on i wszystkie jego nadklasy muszą być zgodne z dość złożonym, niewykonalnym iw dużej mierze nieudokumentowanym protokołem. "


2
Jeśli obiekt nie jest klonowalny, funkcja clone () obiektu zgłosi wyjątek CloneNotSupportedException. Więc musisz być klonowalny, jeśli chcesz wywołać super.clone () (co spowoduje wywołanie Object.clone ()). Nie widzę, jak można serializować obiekt bez implementacji Serializable.
Steve Kuo

1
„Myślę, że projekt Cloneable jest obecnie w dużej mierze uważany za błąd”. [potrzebne źródło]
Kevin Panko,

Przepraszam - nie sugerowałem tego. Ja tylko sugeruje, że „dobry” projekt jest nie uczynić interfejs przedłużyć Serializable- to do implementacji zdecydować, czy wdrożyć Serializable. Rozszerzyłem to na Cloneable- nie jest to coś, co interfejs powinien rozszerzać - ale implementacja interfejsu jest wolna Cloneable. Problem w tym, że jeśli masz parametr typu interfejsu, pytasz go, czy można go sklonować; ale wtedy nie możesz tego sklonować!
oxbow_lakes

6
@Kevin - efektywna Java pp45 Josha Blocha . „Interfejs Cloneable został pomyślany jako interfejs do mieszania obiektów w celu ogłaszania, że ​​zezwalają na klonowanie. Niestety nie spełnia tego celu”
oxbow_lakes,

Również na tej samej stronie: „Jest to wysoce nietypowe użycie interfejsów, a nie taki, który ma być emulowany” oraz „Aby zaimplementowanie interfejsu miało jakikolwiek wpływ na klasę, on i wszystkie jego nadklasy muszą być zgodne z dość złożonym, niewykonalny iw dużej mierze nieudokumentowany protokół "
oxbow_lakes

30

Interfejs Clonable to tylko wskaźnik mówiący, że klasa może obsługiwać klon. Metoda jest chroniona, ponieważ nie powinieneś wywoływać jej na obiekcie, możesz (i powinieneś) nadpisać ją jako publiczną.

Od słońca:

W klasie Object metoda clone () jest deklarowana jako chroniona. Jeśli wszystko, co zrobisz, to zaimplementowanie Cloneable, tylko podklasy i elementy tego samego pakietu będą mogły wywołać clone () na obiekcie. Aby umożliwić dowolnej klasie w dowolnym pakiecie dostęp do metody clone (), musisz ją przesłonić i zadeklarować jako publiczną, tak jak pokazano poniżej. (Gdy nadpisujesz metodę, możesz uczynić ją mniej prywatną, ale nie bardziej prywatną. W tym przypadku chroniona metoda clone () w Object jest zastępowana jako metoda publiczna).


Co jest w porządku, dopóki nieSet
dodasz

@oxbow_lakes: ale może niektórych implementacji Seta nie da się sklonować
newacct

3
Nie możesz sklonować niczego, co nie implementuje interfejsu Clonable - jest to znacznik, który mówi „Ta klasa jest poprawnie klonowalna” - bardzo podobny do interfejsu Serializable. Nawiasem mówiąc, istnieje sposób na klonowanie klas przez serializację, który działa dobrze - wygoogluj coś takiego jak „klon serializacji java” i prawdopodobnie znajdziesz kilka sposobów na uzyskanie głębokiej kopii swojego obiektu.
Bill K

4
Nie możesz sklonować niczego, co nie implementuje interfejsu Cloneable, ale tylko dlatego, że coś implementuje interfejs Cloneable, nie oznacza, że ​​możesz to sklonować.
Michael Myers

1
@BuckCherry toString ma domyślną implementację, jeśli nazwiesz to coś dobrego, wydarzy się coś dobrego i otrzymasz ciąg znaków. equals ma domyślną implementację (tak samo jak ==). Klon nie może mieć domyślnej implementacji. Jeśli wywołasz clone na obiekcie, który go nie zaimplementował, nie uzyskasz rozsądnego zachowania. Klonowanie jest skomplikowane i tak naprawdę nie może być wykonywane automatycznie (niektóre obiekty bez domyślnych konstruktorów mogą być niemożliwe do sklonowania generalnie), więc domyślnie sprawiają, że jest trochę bezpieczniejsze. Myślę jednak, że umieszczenie go na Object w ogóle mogło nie być konieczne.
Bill K

7

clonejest chroniony, ponieważ jest to coś, co powinno zostać zastąpione, aby było specyficzne dla bieżącej klasy. Chociaż byłoby możliwe utworzenie clonemetody publicznej , która w ogóle sklonowałaby dowolny obiekt, nie byłaby tak dobra, jak metoda napisana specjalnie dla klasy, która tego potrzebuje.


ale dlaczego należy go przed tym chronić?
Janusz

3
Jest chroniony, więc nie używasz tego w obiekcie (i tak wyrzuci wyjątek). Chcą, abyś zastąpił to w klasie, a następnie upublicznij. (odpowiedział kilka razy poniżej)
Bill K,

1
I bezużyteczne, biorąc pod uwagę interfejsy, jak w moim punkcie poniżej
oxbow_lakes,

powinno być zastąpione, nie jest zbyt jasne, to powinno być abstrakcyjne, a następnie nie może być w klasie obiektów, staram się zrozumieć, dlaczego tak jest.
Kumar Abhishek

4

Metoda Clone nie może być używana bezpośrednio na żadnym obiekcie, dlatego jest przeznaczona do zastąpienia przez podklasę.

Oczywiście mogłoby to być publiczne i po prostu zgłosić odpowiedni wyjątek, gdy klonowanie nie jest możliwe, ale myślę, że byłoby to mylące.

Sposób, w jaki klon jest teraz zaimplementowany, sprawia, że ​​myślisz o tym, dlaczego chcesz użyć klonowania i jak chcesz, aby twój obiekt był klonowany.


2

Jest chroniony, ponieważ domyślna implementacja tworzy płytką kopię składową wszystkich pól (w tym prywatnych), omijając konstruktora . Nie jest to coś, do czego obiekt mógłby zostać zaprojektowany w pierwszej kolejności (na przykład może śledzić instancje utworzonych obiektów na liście współdzielonej lub coś podobnego).

Z tego samego powodu domyślna implementacja clone()spowoduje zgłoszenie, jeśli wywoływany obiekt nie jest implementowany Cloneable. Jest to potencjalnie niebezpieczna operacja o dalekosiężnych konsekwencjach, dlatego autor klasy musi wyraźnie wyrazić zgodę.


Właściwie domyślna implementacja (w obiekcie) zgłasza wyjątek zgodnie z dokumentacją ...
Bill K

2
Nie, to nie tylko rzuca. Z jego JavaDoc: „Metoda clone dla klasy Object wykonuje określoną operację klonowania. Po pierwsze, jeśli klasa tego obiektu nie implementuje interfejsu Cloneable, generowany jest wyjątek CloneNotSupportedException. Należy zauważyć, że wszystkie tablice są traktowane jako implementujące interfejs Cloneable. W przeciwnym razie ta metoda tworzy nowe wystąpienie klasy tego obiektu i inicjuje wszystkie jego pola z dokładnie zawartością odpowiednich pól tego obiektu, tak jakby przez przypisanie; zawartość pól nie jest sama w sobie klonowana. "
Pavel Minaev,

2

Z javadoc pliku cloneable.

* By convention, classes that implement this interface (cloneable) should override 
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.

* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface.  Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.

Możesz więc wywołać clone na każdym obiekcie, ale przez większość czasu nie da ci to pożądanych wyników ani wyjątku. Ale jest zalecane tylko wtedy, gdy zaimplementujesz cloneable.


2
Nie możesz wywołać clone na każdym obiekcie, ponieważ jest on chroniony!
Pavel Minaev,

2

IMHO to takie proste:

  • #clone nie mogą być wywoływane na obiektach nie dających się klonować, dlatego nie są upubliczniane
  • #clonemusi być wywoływana przez podklasy ob, Objectktóre implementują Cloneable, aby uzyskać płytką kopię odpowiedniej klasy

Jaki jest właściwy zakres dla metod, które mają być wywoływane przez podklasy, ale nie przez inne klasy?

To jest protected.

Klasy implementujące, Cloneableoczywiście, upublicznią tę metodę, aby można ją było wywołać z innych klas.


0

Metoda Clone () ma wewnętrzną kontrolę „instancja klonowalna czy nie”. W ten sposób zespół Java może sądzić, że ograniczy niewłaściwe użycie metody clone (). Metoda clone () jest chroniona, tj. Dostęp do niej mają tylko podklasy. Ponieważ obiekt jest klasą nadrzędną wszystkich podklas, więc metoda Clone () może być używana przez wszystkie klasy, jeśli nie mamy powyższego sprawdzenia 'instancji Cloneable'. To jest powód, dla którego zespół Java mógł pomyśleć o ograniczeniu niewłaściwego użycia funkcji clone () poprzez sprawdzenie w metodzie clone () „czy jest to instancja Cloneable”.

Stąd wszystkie klasy implementujące cloneable mogą używać metody clone () klasy Object.

Ponieważ jest chroniony, jest dostępny tylko dla tych podklas, które implementują klonowalny interfejs. Jeśli chcemy, aby była publiczna, ta metoda musi zostać zastąpiona przez podklasę z jej własną implementacją.


-2

Tak, ten sam problem, z którym się spotkałem. Ale rozwiązuję to, implementując ten kod

public class Side implements Cloneable {
    public Side clone() {

        Side side = null;
        try {
            side = (Side) super.clone();
        } catch (CloneNotSupportedException e) {
            System.err.println(e);
        }
        return side;
    }
}

Tak jak wcześniej ktoś powiedział.


1
CloneNotSupportedException to kolejny przykład sprawdzonego wyjątku, który powinien być odznaczony (to znaczy powinien rozszerzać RuntimeException, a nie Exception). Chociaż metoda clone () w klasie Side implementuje Cloneable i dlatego nigdy nie zgłosi CloneNotSupportedException, Side.clone () musi nadal przechwytywać lub deklarować wyjątek. To po prostu dodaje zbędny szum obsługi wyjątków do metody clone ().
Derek Mahar

-2

Cóż, także programiści słońca są tylko ludźmi i rzeczywiście popełnili ogromny błąd, implementując metodę klonowania jako chronioną, ten sam błąd, który zaimplementowali niedziałającą metodę klonowania w ArrayList! Tak więc, ogólnie rzecz biorąc, nawet doświadczeni programiści Javy nie rozumieją metody klonowania.

Jednak ostatnio znalazłem szybkie i łatwe rozwiązanie do kopiowania dowolnego obiektu z całą zawartością, bez względu na to, jak jest zbudowany i co zawiera, zobacz moją odpowiedź tutaj: Błąd w używaniu Object.clone ()


-3

Ponownie, framework Java JDK pokazuje genialne myślenie:

Interfejs klonowalny nie zawiera "public T clone ();" metoda, ponieważ działa bardziej jak atrybut (np. serializowalny), który umożliwia sklonowanie instancji.

Nie ma nic złego w tym projekcie, ponieważ:

  1. Object.clone () nie zrobi tego, co chcesz z klasą zdefiniowaną przez użytkownika.

  2. Jeśli masz implementację Myclass Cloneable => nadpisujesz clone () przez "public MyClass clone ()"

  3. Jeśli masz MyInterface rozszerza Cloneable i niektóre MyClasses implementujące MyInterface: po prostu zdefiniuj "public MyInterface clone ();" w interfejsie i każda metoda używająca obiektów MyInterface będzie mogła je sklonować, niezależnie od ich klasy MyClass.


2
jeśli twoja klasa zostanie odziedziczona, implementacja klonu klasy pochodnej nie będzie bezpieczna, dopóki implementacje klasy bazowej nie zapewnią bezpiecznych metod klonowania. Poza tym, posiadanie interfejsu bez metody / atrybutu jest czymś niezwykłym. Ten interfejs nie wymusza na klasie implementacji clone.
prap19
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.