Dlaczego Cloneable nie jest przestarzałe?

139

Powszechnie wiadomo, że Cloneableinterfejs w Javie jest uszkodzony. Jest ku temu wiele powodów, o których nie wspomnę; inni już to zrobili. Jest to również stanowisko samych architektów Javy .

Moje pytanie brzmi zatem: dlaczego nie zostało jeszcze wycofane? Jeśli główny zespół Java zdecydował, że jest uszkodzony, musiał również rozważyć jego wycofanie. Jakie są ich powody, aby to zrobić (w Javie 8 nadal nie jest przestarzałe )?

Kao
źródło
41
Kwestia ta nie jest „oparta głównie na opinii”, ponieważ wielu najwyraźniej czuje się uprawnionych do osądzania. Ci, którzy mają tylko opinię na temat powodów, po prostu nie mają kwalifikacji, aby odpowiedzieć. Jednak prawdą jest, że masz tu tylko niewielką szansę na uzyskanie miarodajnej odpowiedzi. Prawdą jest również, że twoje pytanie nie dotyczy problemu, który masz do rozwiązania, więc jest przynajmniej na pograniczu tematu.
Marko Topolnik
6
@MarkoTopolnik Zgadzam się, że są ludzie na świecie, którzy mogliby udzielić miarodajnej odpowiedzi, ale nie sądzę, aby to był test, który tutaj stosujemy. Powód zamknięcia stwierdza, że ​​„odpowiedzi na to pytanie będą prawie w całości oparte na opiniach”. Podejrzewam, że tak będzie w tym przypadku, chyba że będziemy mieli szczęście.
Duncan Jones
2
Oto „Jak i kiedy” wycofać z Oracle… ( docs.oracle.com/javase/6/docs/technotes/guides/javadoc/… ) Interfejs klonowalny może znaleźć się w „ błędnym lub bardzo nieefektywnym” przypadku, ale jest bardzo otwarta na opinie.
Maxx
8
@Duncan Nadal nie uważam za sprawiedliwe osądzanie tego pytania na podstawie moich przypuszczeń o braku dyscypliny ze strony respondentów . Jeśli użytkownik nie zna powodu, o który jest pytany, nie jest uprawniony do nadużycia automatycznej sekretarki w celu przedstawienia swojej opinii w tej sprawie.
Marko Topolnik
4
@lexicore Tak, dokładnie - i możesz się założyć, że dokładnie rozważyli tę opcję i co za tym idzie, muszą mieć mocne powody, aby jej nie uznawać za przestarzałą. CloneablePowszechnie znana jest ich własna krytyka .
Marko Topolnik

Odpowiedzi:

120

Jest bug złożony w 1997 roku do Java Database Bug o dodanie clone()metody Cloneable, więc to nie będzie bezużyteczna. Został zamknięty postanowieniem „nie naprawi”, a uzasadnienie było następujące:

Komitet ds. Przeglądu technicznego firmy Sun (TRC) szczegółowo rozważył ten problem i odradzał podejmowanie jakichkolwiek działań poza ulepszaniem dokumentacji obecnego interfejsu Cloneable . Oto pełny tekst zalecenia:

Istniejące interfejsy API do klonowania obiektów Java są problematyczne. Na java.lang.Object istnieje chroniona metoda „klonowania” oraz interfejs java.lang.Cloneable. Intencją jest to, że jeśli klasa chce pozwolić innym ludziom na jej sklonowanie, powinna obsługiwać interfejs Cloneable i zastąpić domyślną chronioną metodę klonowania publiczną metodą klonowania. Niestety, z powodów wygodnie zagubionych w mgle czasu, interfejs Cloneable nie definiuje metody klonowania.

Ta kombinacja powoduje spore zamieszanie. Niektóre klasy twierdzą, że obsługują Cloneable, ale przypadkowo zapominają o obsłudze metody clone. Deweloperzy są zdezorientowani co do tego, jak powinien działać Cloneable i co powinien zrobić klon.

Niestety dodanie metody „clone” do Cloneable byłoby niekompatybilną zmianą. Nie naruszy to zgodności binarnej, ale złamie zgodność ze źródłami. Anegdotyczne dowody sugerują, że w praktyce istnieje wiele przypadków, w których klasy obsługują interfejs Cloneable, ale nie zapewniają publicznej metody klonowania. Po dyskusji, TRC jednogłośnie zalecił, aby NIE modyfikować istniejącego interfejsu Cloneable ze względu na wpływ na zgodność.

Alternatywną propozycją było dodanie nowego interfejsu java.lang.PubliclyCloneable, aby odzwierciedlić pierwotne przeznaczenie Cloneable. Od 5 do 2 głosów TRC odradziła to. Głównym problemem było to, że dodałoby to jeszcze więcej zamieszania (w tym zamieszania w pisowni!) Do już zagmatwanego obrazu.

TRC jednogłośnie zaleciła dodanie dodatkowej dokumentacji do istniejącego interfejsu Cloneable, aby lepiej opisać sposób jego użycia i opisać „najlepsze praktyki” dla osób wdrażających.

Tak więc, chociaż nie chodzi bezpośrednio o wycofanie z użycia , powodem tego, że Cloneable nie jest „przestarzały”, jest fakt, że komitet ds. Przeglądu technicznego zdecydował, że modyfikacja istniejącej dokumentacji będzie wystarczająca, aby uczynić ten interfejs użytecznym. I tak zrobili. Aż do wersji Java 1.4 Cloneabledokumentowano w następujący sposób:

Klasa implementuje interfejs Cloneable, aby wskazać metodzie Object.clone (), że metoda ta może tworzyć kopie instancji tej klasy w postaci pola dla pola.

Próby klonowania wystąpień, które nie implementują interfejsu Cloneable, powodują zgłoszenie wyjątku CloneNotSupportedException.

Interfejs Cloneable nie deklaruje żadnych metod.

Od wersji Java 1.4 (która została wydana w lutym 2002) do aktualnej edycji (Java 8) wygląda to tak:

Klasa implementuje interfejs Cloneable, aby wskazać metodzie Object.clone (), że metoda ta może tworzyć kopie instancji tej klasy w postaci pola dla pola. Wywołanie metody clone Object na wystąpieniu, które nie implementuje interfejsu Cloneable, powoduje zgłoszenie wyjątku CloneNotSupportedException.

Zgodnie z konwencją klasy, które implementują ten interfejs, powinny przesłonić Object.clone (który jest chroniony) metodą publiczną. Aby uzyskać szczegółowe informacje na temat zastępowania tej metody, zobacz Object.clone ().

Zauważ, że ten interfejs nie zawiera metody klonowania. Dlatego nie jest możliwe sklonowanie obiektu jedynie ze względu na fakt, że implementuje on ten interfejs. Nawet jeśli metoda clone jest wywoływana refleksyjnie, nie ma gwarancji, że się powiedzie.

Kao
źródło
3
i czy wiesz, dlaczego ta clonemetoda była Objectna pierwszym miejscu?
njzk2
8
@ njzk2 To zasadnicza część mechanizmu - jest to metoda realizująca niskopoziomową, pozajęzykową magię kopiowania obrazu obiektu bit po bicie.
Marko Topolnik
3
@Unheilig Ta magia jest czymś, czego nie można powielić konstruktorem kopiującym: Object#clone()tworzy instancję tej samej klasy, co oryginał, bez znajomości tej klasy w czasie kompilacji.
Marko Topolnik
4
@AVolpe: To nie naprawiłoby niezgodności źródła wspomnianej w odpowiedzi „gdzie klasy obsługują interfejs Cloneable, ale nie zapewniają publicznej metody klonowania”. W szczególności klasy zapewniające niepubliczną metodę klonowania zostałyby zepsute.
Louis Wasserman,
2
Niezła historia. Oto bezpośredni link do bazy danych błędów. Dodałem tam więcej historii i zacytowałem jej fragmenty w mojej odpowiedzi .
Stuart Marks
64

Krótka odpowiedź na pytanie „dlaczego nie jest Cloneableprzestarzała?” (a właściwie, dlaczego nie są one Xprzestarzałe, dla żadnego X) jest takie, że nie poświęcono zbyt wiele uwagi ich wycofaniu.

Większość rzeczy, które zostały ostatnio wycofane, zostały wycofane, ponieważ istnieje określony plan ich usunięcia. Na przykład metody addPropertyChangeListeneri LogManagera zostały wycofane w Javie SE 8 z zamiarem usunięcia ich w Javie SE 9. (Przyczyną jest to, że niepotrzebnie komplikują one współzależności modułów). Rzeczywiście, te API zostały już usunięte z wczesnego rozwoju JDK 9 buduje. (Zauważ, że podobne wywołania detektora zmian właściwości zostały również usunięte z ; patrz JDK-8029806 ).removePropertyChangeListenerPack200

Nie istnieje podobny plan dla Cloneablei Object.clone().

Dłuższa odpowiedź wymagałaby omówienia dalszych pytań, takich jak to, czego można się spodziewać po tych interfejsach API, jakie koszty lub korzyści narosłyby platforma, gdyby zostały wycofane, i co jest przekazywane programistom, gdy interfejs API jest przestarzały. Omówiłem ten temat w moim ostatnim wykładzie JavaOne, Debt and Deprecation . (Slajdy dostępne pod tym linkiem; wideo tutaj ). Okazuje się, że sam JDK nie był zbyt konsekwentny w używaniu wycofania. Kiedyś oznaczało kilka różnych rzeczy, w tym na przykład

  • Jest to niebezpieczne i należy zdawać sobie sprawę z ryzyka związanego z użyciem (przykład: Thread.stop(), Thread.resume(), i Thread.suspend()).

  • To zostanie usunięte w przyszłej wersji

  • To jest przestarzałe i dobrym pomysłem jest użycie czegoś innego (przykład: wiele metod w java.util.Date)

Wszystkie mają różne znaczenia, a różne ich podzbiory mają zastosowanie do różnych przestarzałych rzeczy. Niektóre z nich mają zastosowanie do rzeczy, które nie są przestarzałe (ale które mogą być przestarzałe).

Cloneablei Object.clone()są „zepsute” w tym sensie, że mają wady konstrukcyjne i są trudne w użyciu. Jednak clone()nadal jest to najlepszy sposób kopiowania tablic, a klonowanie ma pewną ograniczoną przydatność do tworzenia kopii instancji klas, które są starannie zaimplementowane. Usunięcie klonowania byłoby niekompatybilną zmianą, która zepsułaby wiele rzeczy. Operację klonowania można by ponownie zaimplementować w inny sposób, ale prawdopodobnie byłoby to wolniejsze niż Object.clone().

Jednak dla większości rzeczy konstruktor kopiujący jest lepszy niż klonowanie. Być może więc oznaczenie Cloneablejako „przestarzałe” lub „zastąpione” lub coś podobnego byłoby właściwe. To powiedziałoby programistom, że prawdopodobnie chcą szukać gdzie indziej, ale nie oznaczałoby to, że mechanizm klonowania może zostać usunięty w przyszłej wersji. Niestety takiego znacznika nie ma.

W obecnym stanie rzeczy „wycofanie z użycia” wydaje się oznaczać ostateczne usunięcie - pomimo faktu, że zniknęła znikoma liczba przestarzałych funkcji, które zostały kiedykolwiek usunięte - i dlatego wycofanie nie wydaje się uzasadnione w przypadku mechanizmu klonowania. Być może w przyszłości będzie można zastosować alternatywne oznakowanie, które nakłania programistów do stosowania alternatywnych mechanizmów.

AKTUALIZACJA

Dodałem dodatkową historię do raportu o błędzie . Frank Yellin, wczesny implementator JVM i współautor specyfikacji JVM, poczynił kilka komentarzy w odpowiedzi na komentarz „zagubiony we mgle czasu” w rekomendacji TRC cytowanej w drugiej odpowiedzi . Zacytowałem tutaj odpowiednie fragmenty; pełna wiadomość znajduje się w zgłoszeniu błędu.

Cloneable nie ma metod z tego samego powodu co Serializable. Cloneable wskazuje właściwość klasy, zamiast mówić cokolwiek o metodach obsługiwanych przez klasę.

Przed refleksją potrzebowaliśmy natywnej metody do wykonania płytkiej kopii obiektu. Tak narodził się Object.clone (). Było również jasne, że wiele klas chciałoby zastąpić tę metodę i że nie każda klasa chciałaby zostać sklonowana. Dlatego narodził się Cloneable, aby wskazać zamiary programisty.

Krótko mówiąc. Celem Cloneable nie było wskazanie, że masz publiczną metodę clone (). Miało to na celu wskazanie, że chcesz zostać sklonowany za pomocą Object.clone (), a implementacja zadecydowała, czy upublicznić clone ().

Stuart Marks
źródło
3
Jedna dobra odpowiedź, sir. Szczególnie podoba mi się to, że nie tylko wrzucasz Object.clone()do ognia tylko dlatego, że wszyscy tego chcą, ale jesteś skłonny uzasadnić i wspomnieć o dobrych rzeczach.
icza
2
Jednak clone () jest nadal najlepszym sposobem kopiowania tablic, a klonowanie ma pewną ograniczoną przydatność do tworzenia kopii wystąpień klas, które są starannie zaimplementowane. Byłem pod wrażeniem poprawki 6428387, wszystkie ścieżki kodu (clone, new / arrayCopy, Arrays.copyOf) dawały te same elementy wewnętrzne. Czy ostatnio coś się zmieniło?
bestsss
2
@bestsss Myślę, że array.clone()niekoniecznie jest to szybsze niż jakiekolwiek alternatywy. Z punktu widzenia API jest to najbardziej zwięzły sposób na zduplikowanie tablicy. Arrays.copyOf(array, newlen)zbliża się, ale wymaga parametru długości, który jest zbędny, jeśli nie zmieniasz długości.
Stuart Marks
2
@Holger Tak, o ile widzimy, jest to pierwsze faktyczne usunięcie API od 1.1. Zauważ również, że chociaż zgadzamy się, że Thread.suspend()i Thread.stop()(no-arg) są niebezpieczne, prawdopodobnie nie zostaną one usunięte - ani zmienione, aby bezwarunkowo zgłosić wyjątek - ponieważ ludzie faktycznie ich używają! Przypuszczalnie są gotowi ponieść ryzyko. Jednym z czynników ograniczających nasłuchiwanie zmian właściwości jest to, że były one używane bardzo rzadko, więc wpływ ich usunięcia jest niewielki.
Stuart Marks
2
@Holger Koncepcyjnie java.beansmógłby zostać uniezależniony, java.desktopponieważ ziarna są tylko biblioteką API dla właściwości. Niestety, jeśli zagłębisz się w API Bean, istnieje wiele zależności od AWT. Wdrożenie ma jeszcze więcej. Z pewnością można je wydobyć, ale wykonanie tego wydaje się dużo bardziej pracochłonne niż, powiedzmy, wyłapywanie kłód z fasoli. Cały wysiłek związany z modularyzacją polega na tym rozplątywaniu; niewątpliwie można by zrobić więcej, ale wtedy Jigsaw trwałoby jeszcze dłużej.
Stuart Marks
-1

dlaczego nie jest jeszcze przestarzałe?

Ponieważ JCP nie uznał tego za stosowne i może nigdy tego nie uczynić. Poprosić ich. Pytasz w złym miejscu.

Jakie są powody utrzymywania tego w Java API

Nikt nigdy nie usunie niczego z Java API z powodu wymogu kompatybilności wstecznej. Ostatnim razem, kiedy to się stało, była zmiana modelu zdarzenia AWT pomiędzy 1.0 a 1.1 w 1996/7.

Markiz Lorne
źródło
17
Usunęli (skutecznie) Thread.stop(Throwable), zmieniając jego kontrakt, aby zawsze rzucał UnsupportedOperationExceptiondo wywołującego (nie do wątku docelowego!).
Marko Topolnik
22
Co wydarzyło się mniej więcej w tym samym czasie? Usunięcie Thread.stop(Throwable)funkcjonalności miało miejsce w Javie 8. W każdym razie, niekwalifikowana rada, aby „zapytać ich” jest błędna, ponieważ sam główny architekt Javy jest aktywnym członkiem Stack Overflow. Po prostu nie zawraca sobie głowy odpowiadaniem na nic poza pytaniami dotyczącymi strumieni.
Marko Topolnik
13
Co więcej, pytanie OP nie dotyczy usunięcia , ale wycofania , i ewidentnie deprecjacja miała miejsce przez cały czas.
Marko Topolnik
17
@EJP Nie pytam, czy Cloneablezostanie usunięty z Java API. Pytam, dlaczego nie zostanie pozbawiony. Myślę, że to idealne miejsce na zadawanie pytań.
Kao
5
@VaheHarutyunyan Dzięki za wiadomość, ale nie jestem architektem Java. Jestem inżynierem w grupie Oracle JDK, która zajmuje się tym wszystkim.
Stuart Marks