Dlaczego "final" nie jest dozwolone w metodach interfejsu Java 8?

Jedną z najbardziej przydatnych funkcji Javy 8 są nowe metody default na interfejsach. Istnieją zasadniczo dwa powody (mogą być inne), dla których zostały wprowadzone: {]}

Z punktu widzenia projektanta API chciałbym móc używać innych modyfikatorów w metodach interfejsu, np. final. To byłoby użyteczna przy dodawaniu wygodnych metod, zapobiegająca "przypadkowym" nadpisaniom w klasach implementujących:

interface Sender {

    // Convenience method to send an empty message
    default final void send() {
        send(null);
    }

    // Implementations should only implement this method
    void send(String message);
}

Powyższe jest już powszechną praktyką, Jeśli Sender były klasą:

abstract class Sender {

    // Convenience method to send an empty message
    final void send() {
        send(null);
    }

    // Implementations should only implement this method
    abstract void send(String message);
}

Teraz, default i finalsą oczywiście sprzeczne słowa kluczowe, ale domyślne słowo kluczowe nie byłoby ściśle wymagane, więc zakładam, że ta sprzeczność jest celowa, aby odzwierciedlić subtelne różnice między "metody klasowe z ciałem" (tylko metody) i "metody". "metody interfejsu z ciałem" (metody domyślne), tzn. różnice, których jeszcze nie zrozumiałem.

W pewnym momencie Wsparcie dla modyfikatorów, takich jak static i final w metodach interfejsu, nie zostało jeszcze w pełni zbadane, cytując Briana Goetza:

Druga część to to, jak daleko posuniemy się, aby wspierać budowanie klas narzędzi w interfejsach, takich jak metody końcowe, metody prywatne, chronione metody, metody statyczne itp. Odpowiedź brzmi: nie know yet

Od tego czasu pod koniec 2011 roku dodano oczywiście obsługę metod static w interfejsach. Oczywiście dodało to wiele wartości do samych bibliotek JDK, takich jak z Comparator.comparing().

Pytanie:

Jaki jest powód final (a także static final) nigdy nie dotarł do interfejsów Java 8?

Author: Robert Harvey, 2014-05-04

4 answers

To pytanie jest w pewnym stopniu związane z jaki jest powód, dla którego" synchronizacja " nie jest dozwolona w metodach interfejsu Java 8?

Kluczową rzeczą do zrozumienia na temat metod domyślnych jest to, że głównym celem projektu jest ewolucja interfejsu, a nie "przekształcenie interfejsów w (mierne) cechy". Chociaż istnieje pewne nakładanie się między nimi, a my staraliśmy się dostosować do tego ostatniego, gdzie nie przeszkadzało to pierwszemu, te pytania najlepiej zrozumieć patrząc w tym świetle. (Zauważ również, że metody klas będą różniły się od metod interfejsu, bez względu na intencję, ze względu na fakt, że metody interfejsu mogą być dziedziczone.)

Podstawową ideą metody domyślnej jest: jest to metoda interfejsu z domyślną implementacją, a klasa pochodna może dostarczyć bardziej szczegółową implementację. A ponieważ Centrum Projektowe było ewolucją interfejsu, krytycznym celem projektu było domyślne metody możliwość dodania do interfejsów po fakcie w sposób zgodny ze źródłami i binarny.

Zbyt prosta odpowiedź na "why not final default methods" jest taka, że wtedy ciało nie będzie po prostu domyślną implementacją, tylko jedyną implementacją. Chociaż jest to trochę zbyt prosta odpowiedź, daje nam wskazówkę, że pytanie zmierza już w wątpliwym kierunku.

Innym powodem, dla którego ostateczne metody interfejsu są wątpliwe jest to, że stwarzają one niemożliwe problemy dla implementatorów. Na przykład, przypuśćmy, że masz:

interface A { 
    default void foo() { ... }
}

interface B { 
}

class C implements A, B { 
}

Tutaj wszystko jest dobre; C dziedziczy foo() z A. Teraz przypuśćmy, że B jest zmieniona na metodę foo, z domyślną wartością:

interface B { 
    default void foo() { ... }
}

Teraz, gdy przechodzimy do rekompilacji C, kompilator powie nam, że nie wie, jakie zachowanie dziedziczyć dla foo(), więc C musi je nadpisać (i może wybrać delegowanie do A.super.foo(), jeśli chce zachować to samo zachowanie.) Ale co by było, gdyby B dokonało domyślnego final, a A nie było pod kontrolą autora C? Teraz {[2] } jest nieodwracalnie zepsuty; nie może skompilować się bez nadpisania foo(), ale nie może nadpisać foo(), jeśli był ostateczny w B.

To tylko jeden przykład, ale chodzi o to, że ostateczne metody są tak naprawdę narzędziem, które ma większy sens w świecie klas jednoklasowego dziedziczenia (ogólnie, które łączą stan zachowania), niż interfejsów, które tylko przyczyniają się do zachowanie i może być mnożyć dziedziczone. Zbyt trudno jest rozumować "jakie inne interfejsy mogą zostać zmieszane z ewentualnym implementatorem", a pozwolenie na ostateczną metodę interfejsu prawdopodobnie spowodowałoby te problemy (i wybuchałyby nie na osobie, która napisała interfejs, ale na biednym użytkowniku, który próbuje go zaimplementować.)

Innym powodem, by ich nie akceptować, jest to, że nie znaczą tego, co myślisz, że znaczą. Domyślna implementacja jest brana pod uwagę tylko wtedy, gdy klasa (lub jego superklasy) nie dostarczają deklaracji (konkretnej lub abstrakcyjnej) metody. Jeśli domyślna metoda byłaby ostateczna, ale Klasa nadrzędna już zaimplementowała metodę, domyślna byłaby ignorowana, co prawdopodobnie nie jest tym, czego oczekiwał autor domyślnej deklaracji ostatecznej. (To zachowanie dziedziczenia jest odzwierciedleniem Centrum projektowania metod domyślnych-ewolucja interfejsu. Powinno być możliwe dodanie domyślnej metody (lub domyślnej implementacji do istniejącej metody interfejsu) do istniejących interfejsów, które mają już implementacje, bez zmiany zachowania istniejących klas implementujących interfejs, gwarantując, że klasy, które już działały przed dodaniem domyślnych metod, będą działać w ten sam sposób w obecności domyślnych metod.)
 358
Author: Brian Goetz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-25 18:23:34

Na liście dyskusyjnej lambda jest wiele dyskusji na ten temat . Jednym z tych, które wydają się zawierać wiele dyskusji na temat wszystkich tych rzeczy jest następujące: na zróżnicowana widoczność metody interfejsu (był ostateczny obrońcy).

W tej dyskusji Autor pierwotnego pytania zadaje coś bardzo podobnego do twojego pytania:

Decyzja o upublicznieniu wszystkich członków interfejsu była rzeczywiście niefortunna decyzja. Że dowolne wykorzystanie interfejsu w projektowaniu wewnętrznym ujawnia prywatne szczegóły implementacji jest duża.

Jest to trudne do naprawienia bez dodawania niektórych niejasnych lub kompatybilności łamanie niuansów języka. Przerwa w kompatybilności z tym wielkość i potencjalna subtelność postrzegana byłaby jako niekonsekwentna, więc rozwiązanie musi istnieć, które nie łamie istniejącego kodu.

Czy ponowne wprowadzenie słowa kluczowego 'pakiet' jako access-specifier może być / align = "left" / To brak konkretów w interfejs oznaczałby publiczne-dostęp i brak specyfika w klasie implikuje dostęp do pakietu. Które konkrety mają sens w interfejsie nie jest jasne - zwłaszcza jeśli, aby zminimalizować obciążenie wiedzą dla deweloperów, musimy zadbać o to, aby access-specifiiers oznaczał to samo w obu klasa i interfejs, jeśli są obecne.

Wobec braku domyślnych metod spekulowałbym, że specyfik elementu w interfejsie musi być co najmniej tak widoczny jak na samego interfejsu (tak więc interfejs może być faktycznie zaimplementowany w wszystkie widoczne konteksty) - z domyślnymi metodami, które nie są tak pewne.

Czy była jakaś wyraźna informacja, czy jest to nawet możliwa dyskusja w zakresie? Jeśli nie, powinien być przechowywany gdzie indziej.

Ostatecznie odpowiedź Briana Goetza brzmiała:

Tak, to już jest badane.

Jednak pozwól mi ustawić kilka realistycznych oczekiwań -- język / VM funkcje mają długi czas realizacji, nawet takie trywialne, pozornie. Czas na propozycje nowych funkcji językowych dla Javy SE 8 ma prawie minęło.

Więc najprawdopodobniej nigdy nie został wdrożony, ponieważ nigdy nie był częścią zakresu. Nigdy nie został zaproponowany w terminie do rozważenia.

W kolejnej gorącej dyskusji o metodach final defender na ten temat, Brian powiedział ponownie :

I dostałaś dokładnie to, czego pragnęłaś za. To jest dokładnie to, co ta funkcja dodaje -- wielokrotne dziedziczenie zachowania. Oczywiście, że my zrozum, że ludzie będą używać ich jako cech. I ciężko pracowaliśmy aby oferowany przez nich model dziedziczenia był prosty i na tyle czyste, że ludzie mogą uzyskać dobre wyniki robiąc to w szerokim różnorodność sytuacji. Jednocześnie postanowiliśmy nie naciskać ich poza granicami tego, co działa prosto i czysto, i że prowadzi do "aw, you didn' t go far enough" reakcje w niektórych przypadkach. Ale naprawdę, większość tego wątku wydaje się narzekać, że szkło jest tylko 98%. Wezmę 98% i do roboty!

Więc to wzmacnia moją teorię, że po prostu nie było to częścią zakresu lub częścią ich projektu. To, co zrobili, to zapewnienie wystarczającej funkcjonalności do radzenia sobie z problemami ewolucji API.

 39
Author: Edwin Dalorzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-08 15:40:13

Trudno będzie znaleźć i zidentyfikować "odpowiedź", ponieważ resony wymienione w komentarzach @EJP : na świecie jest około 2 (+/- 2) osób, które w ogóle mogą dać konkretną odpowiedź . I wątpliwości, odpowiedź może być po prostu coś w rodzaju "wspieranie ostatecznych metod domyślnych nie wydaje się być warte wysiłku restrukturyzacji wewnętrznych mechanizmów rozwiązywania połączeń". Są to oczywiście spekulacje, ale przynajmniej są poparte subtelnymi dowodami, takimi jak to [9]}stwierdzenie (przez jedną z dwóch osób) na liście dyskusyjnej OpenJDK :

"przypuszczam, że jeśli dozwolone są metody "final default", mogą one wymagać przepisania z wewnętrznych invokespecial do invokeinterface widocznych przez użytkownika."

I takie trywialne fakty metoda po prostu nie jestuważana za (naprawdę) ostateczną metodę, gdy jest to metoda default, obecnie zaimplementowana w metodziemetoda::is_final_method w OpenJDK.

Dalej naprawdę "autorskie" informacje są rzeczywiście trudne do znalezienia, nawet przy nadmiernych wyszukiwaniach internetowych i czytaniu logów commitów. Pomyślałem, że może to być związane z potencjalnymi niejasnościami podczas rozwiązywania wywołań metod interfejsu za pomocą Instrukcji invokeinterface oraz wywołań metod klasy, odpowiadających instrukcji invokevirtual: dla instrukcji invokevirtual może być proste wyszukiwanie vtable, ponieważ metoda musi być albo dziedziczona z klasy nadrzędnej, albo zaimplementowana bezpośrednio przez klasę. W w przeciwieństwie do tego, wywołanie invokeinterface musi zbadać odpowiednią stronę wywołania, aby dowiedzieć się , do którego interfejsu odnosi się to wywołanie (jest to wyjaśnione bardziej szczegółowo na stronie InterfaceCalls HotSpot Wiki). Jednak metody final albo nie są wstawiane do vtable w ogóle, albo zastępują istniejące wpisy w vtable (zobacz klassVtable.cpp. Wiersz 333), i podobnie domyślne metody zastępują istniejące wpisy w vtable (patrz klassVtable.cpp, linia 202). Tak więc faktyczny powód (a więc odpowiedź) musi być ukryty głębiej w (dość złożonej) metodzie wywoływania mechanizmów rozwiązywania, ale być może te odniesienia będą jednak uważane za pomocne, czy to tylko dla innych, którzy zdołają uzyskać rzeczywistą odpowiedź z tego.

 15
Author: Marco13,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-04 12:12:40

Nie sądzę, aby konieczne było określanie final metody interfejsu convience, mogę się jednak zgodzić, że Może być pomocna, ale pozornie koszty przewyższają korzyści.

To, co powinieneś zrobić, tak czy inaczej, to napisać poprawne javadoc dla domyślnej metody, pokazując dokładnie, czym jest metoda, a do czego nie jest dozwolone. W ten sposób klasy implementujące interfejs "nie mogą" zmieniać implementacji, choć nie ma Gwarancje.

Każdy może napisać Collection, który przylega do interfejsu, a następnie robi rzeczy w metodach, które są absolutnie sprzeczne intuicyjne, nie ma sposobu, aby się przed tym uchronić, poza pisaniem obszernych testów jednostkowych.

 3
Author: skiwi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-04 12:36:53