Jedną z najbardziej przydatnych funkcji Java 8 są nowe default
metody interfejsów. Istnieją zasadniczo dwa powody (mogą być inne), dlaczego zostały wprowadzone:
- Zapewnienie rzeczywistych domyślnych implementacji. Przykład:
Iterator.remove()
- Zezwolenie na ewolucję API JDK. Przykład:
Iterable.forEach()
Z punktu widzenia projektanta API chciałbym móc korzystać z innych modyfikatorów metod interfejsu, np final
. Przydałoby się to podczas dodawania metod wygody, zapobiegając „przypadkowym” zastąpieniom przy wdrażaniu klas:
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ą, gdyby 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 final
oczywiście są oczywiście sprzeczne słowa kluczowe, ale samo domyślne słowo kluczowe nie byłoby ściśle wymagane , więc zakładam, że ta sprzeczność jest umyślna, aby odzwierciedlić subtelne różnice między „metodami klasy z ciałem” (tylko metody) a „interfejsem” metody z ciałem ” (metody domyślne), tj. różnice, których jeszcze nie rozumiałem.
W pewnym momencie obsługa modyfikatorów takich jak static
i final
metod interfejsu nie została jeszcze w pełni zbadana, powołując się na Briana Goetza :
Drugą częścią jest to, jak daleko posuniemy się do wspierania narzędzi do budowania klas w interfejsach, takich jak metody końcowe, metody prywatne, metody chronione, metody statyczne itp. Odpowiedź brzmi: jeszcze nie wiemy
Od tego czasu pod koniec 2011 roku static
dodano oczywiście obsługę metod w interfejsach. Najwyraźniej dodało to wiele wartości do samych bibliotek JDK, takich jak with Comparator.comparing()
.
Pytanie:
Jaki jest powód final
(i także static final
), że nigdy nie dotarł do interfejsów Java 8?
źródło
final
zapobiega to zastąpieniu metody i widząc, jak MUSISZ przesłonić metody odziedziczone z interfejsów, nie rozumiem, dlaczego miałoby sens nadać jej ostateczność. Chyba że miałoby to oznaczać, że metoda jest ostateczna PO PRZECIWCIENIU jej raz .. W takim przypadku może być jakieś trudności? Jeśli nie rozumiem tego prawa, pozwól mi kmow. Wydaje się interesującefinal
się w tym przypadku zapobieganie zastąpieniu przez klasę domyślną implementację metody interfejsu.Odpowiedzi:
To pytanie jest w pewnym stopniu związane z tym, z jakiego powodu „synchronizacja” jest niedozwolona w metodach interfejsu Java 8?
Kluczową rzeczą do zrozumienia w przypadku metod domyślnych jest to, że głównym celem projektowania jest ewolucja interfejsu , a nie „przekształcanie interfejsów w (mierne) cechy”. Chociaż oba te elementy nakładają się na siebie, a my staraliśmy się dostosować do tych drugich, gdzie nie przeszkadzało to w pierwszym, te pytania najlepiej zrozumieć, patrząc w tym świetle. (Uwaga też, że metody klasy są będzie różna od metod interfejsu, bez względu na zamiar, z racji faktu, że metody interfejsu można mnożyć dziedziczone.)
Podstawową ideą metody domyślnej jest: jest to metoda interfejsu z domyślną implementacją, a klasa pochodna może zapewnić bardziej szczegółową implementację. A ponieważ ośrodek konstrukcja była ewolucja interfejsu, to był krytyczny cel projektu, że metody domyślne mógł być dodany do interfejsów po fakcie , w sposób zgodny źródłowej i binarnej kompatybilne.
Zbyt prosta odpowiedź na pytanie „dlaczego nie ostateczne domyślne metody” jest taka, że wtedy ciało nie byłoby po prostu domyślną implementacją, byłoby jedyną implementacją. Chociaż jest to nieco zbyt prosta odpowiedź, daje nam wskazówkę, że pytanie zmierza już w wątpliwym kierunku.
Innym powodem, dla którego końcowe metody interfejsu są wątpliwe, jest to, że stwarzają one niemożliwe problemy dla implementatorów. Załóżmy na przykład, że masz:
Tutaj wszystko jest dobrze;
C
dziedziczyfoo()
poA
. Teraz załóżmy, żeB
zmieniono nafoo
metodę z domyślną:Teraz, gdy przejdziemy do ponownej kompilacji
C
, kompilator powie nam, że nie wie, dla jakiego zachowania powinien dziedziczyćfoo()
, więcC
musi go zastąpić (i może zdecydować o delegowaniu,A.super.foo()
jeśli chce zachować to samo zachowanie). Ale co, jeśliB
zrobił swoje domyślnefinal
iA
nie jest pod kontrolą autoraC
? TerazC
jest bezpowrotnie zepsuty; nie można go skompilować bezfoo()
przesłonięcia, ale nie można go przesłonić,foo()
jeśli był końcowy wB
.To tylko jeden przykład, ale chodzi o to, że ostateczność metod jest tak naprawdę narzędziem, które ma sens w świecie klas z pojedynczym dziedzictwem (generalnie, które łączą stan z zachowaniem), niż z interfejsami, które jedynie przyczyniają się do zachowania i mogą być mnożone dziedziczny. Zbyt trudno jest uzasadnić, „jakie inne interfejsy mogłyby zostać zmieszane z ostatecznym implementatorem”, a zezwolenie na ostateczną metodę interfejsu prawdopodobnie spowodowałoby te problemy (i wybuchłyby nie na osobie, która napisała interfejs, ale na słaby użytkownik, który próbuje to zaimplementować).
Innym powodem, dla którego należy ich zabronić, jest to, że nie mieliby na myśli tego, co myślisz. Domyślna implementacja jest brana pod uwagę tylko wtedy, gdy klasa (lub jej nadklasy) nie zapewnia deklaracji (konkretnej lub abstrakcyjnej) metody. Gdyby domyślna metoda była ostateczna, ale nadklasa już ją zaimplementowała, domyślna byłaby zignorowana, co prawdopodobnie nie jest tym, czego oczekiwał autor domyślny, deklarując ją jako ostateczną. (To zachowanie dziedziczenia jest odzwierciedleniem centrum projektowania metod domyślnych - ewolucji interfejsu. Powinna istnieć możliwość dodania metody domyślnej (lub domyślnej implementacji do istniejącej metody interfejsu) do istniejących interfejsów, które już mają implementacje, bez zmiany zachowanie istniejących klas, które implementują interfejs,
źródło
Na liście mailingowej lambda jest wiele dyskusji na ten temat . Jednym z tych, które wydają się zawierać wiele dyskusji na temat tego wszystkiego, jest: Widoczność metody zróżnicowanego interfejsu (była Final Defenders) .
W tej dyskusji Talden, autor oryginalnego pytania, zadaje pytanie bardzo podobne do twojego pytania:
Ostatecznie odpowiedź Briana Goetza brzmiała :
Najprawdopodobniej nigdy nie został wdrożony, ponieważ nigdy nie był objęty zakresem. Nigdy nie zaproponowano jej na czas do rozważenia.
W kolejnej gorącej dyskusji na temat ostatecznych metod obrony na ten temat Brian powiedział ponownie :
To umacnia moją teorię, że po prostu nie była częścią zakresu ani części ich projektu. Udało im się zapewnić wystarczającą funkcjonalność, aby poradzić sobie z zagadnieniami ewolucji API.
źródło
Trudno będzie znaleźć i zidentyfikować „the” odpowiedź, dla przyczyn niezależnych wymienionych w komentarzach z @EJP: Istnieje około 2 (+/- 2) ludzi na świecie, którzy mogą dają jednoznacznej odpowiedzi na wszystko . Wątpliwości może być po prostu coś w rodzaju: „Wspieranie ostatecznych domyślnych metod nie wydawało się warte wysiłku polegającego na restrukturyzacji wewnętrznych mechanizmów rozstrzygania połączeń”. Jest to oczywiście spekulacja, ale jest ona poparta przynajmniej subtelnymi dowodami, takimi jak to Oświadczenie (jednej z dwóch osób) z listy mailingowej OpenJDK :
i trywialne fakty, takie jak ta, metoda po prostu nie jest uważana za (naprawdę) ostateczną metodę, gdy jest to
default
metoda, jak obecnie zaimplementowana w metodzie Method :: is_final_method w OpenJDK.Trudno znaleźć dalsze naprawdę „autorytatywne” informacje, nawet przy nadmiernej liczbie wyszukiwań internetowych i czytaniu dzienników zatwierdzeń. Pomyślałem, że może to być związane z potencjalnymi niejednoznacznościami podczas rozwiązywania wywołań metod interfejsu z
invokeinterface
instrukcją i wywołaniami metod klasy, odpowiadającymiinvokevirtual
instrukcji: W przypadkuinvokevirtual
instrukcji może istnieć proste wyszukiwanie vtable , ponieważ metoda musi być albo dziedziczona z nadklasy lub zaimplementowane bezpośrednio przez klasę. W przeciwieństwie do tego,invokeinterface
połączenie musi zbadać odpowiednią stronę wywoływania, aby dowiedzieć się, do którego interfejsu to połączenie faktycznie się odnosi (wyjaśniono to bardziej szczegółowo na stronie InterfaceCalls na HotSpot Wiki). Jednak,final
metody albo w ogóle nie są wstawiane do vtable , albo zastępują istniejące wpisy w vtable (patrz klassVtable.cpp. Linia 333 ), i podobnie, domyślne metody zastępują istniejące wpisy w vtable (patrz klassVtable.cpp, wiersz 202 ) . Tak więc faktyczny powód (a zatem odpowiedź) musi być ukryty głębiej w (raczej złożonym) mechanizmie rozstrzygania wywołań metody, ale być może te odniesienia będą mimo to uważane za pomocne, czy tylko dla innych, którym uda się uzyskać rzeczywistą odpowiedź z tego.źródło
int
, ale patrz stackoverflow.com/questions/430346 ... ...synchronized
nadefault
metodach.final
pytania, i nieco upokarzające jest to, że nikt inny nie myślał o podobnych przykładach (a może niektórzy myśleli o tych przykładach, ale nie czuli się wystarczająco kompetentni, aby odpowiedzieć). Więc teraz (przepraszam, muszę to zrobić :) wypowiedziane jest ostatnie słowo.Nie sądzę, że konieczne jest określenie
final
metody interfejsu konwergencji. Mogę się zgodzić, że może to być pomocne, ale najwyraźniej koszty przeważają nad korzyściami.Niezależnie od tego, co powinieneś zrobić, to napisać odpowiedni javadoc dla domyślnej metody, pokazując dokładnie, czym jest ta metoda i czego nie wolno robić. W ten sposób klasy implementujące interfejs „nie mogą” zmieniać implementacji, chociaż nie ma gwarancji.
Każdy może napisać
Collection
tekst, który jest zgodny z interfejsem, a następnie robi rzeczy metodami absolutnie sprzecznymi z intuicją, nie ma innego sposobu, aby się przed tym uchronić, niż napisanie obszernych testów jednostkowych.źródło
final
którego postanowiono nie zezwalać nainterface
metody Java 8 . Niewystarczający stosunek kosztów do korzyści jest dobrym kandydatem, ale jak dotąd to spekulacja.Dodajemy
default
słowo kluczowe do naszej metody,interface
gdy wiemy, że klasa rozszerzająca naszą implementacjęinterface
może, ale nie musioverride
. Ale co, jeśli chcemy dodać metodę, której nie chcemy przesłonić żadną klasą implementacyjną? Cóż, dwie opcje były dla nas dostępne:default
final
metodę.static
metodę.Teraz Java mówi, że jeśli mamy
class
implementację dwóch lub więcejinterfaces
takich, że mają onedefault
metodę o dokładnie takiej samej nazwie i podpisie, tj. Są duplikaty, wówczas musimy zapewnić implementację tej metody w naszej klasie. Teraz w przypadkudefault
final
metod nie możemy zapewnić implementacji i utknęliśmy. I dlategofinal
słowo kluczowe nie jest używane w interfejsach.źródło