Chronione w interfejsach

111

Dlaczego wszystkie metody w interfacedefinicji są niejawnie public? Dlaczego nie pozwala na protectedmetodę?

Swaranga Sarma
źródło
22
Bardzo dobre pytanie. Dla prawie każdej innej rzeczy w Javie znalazłem prawdziwy powód dla dokonanych wyborów, ale dla tego nie mam. Zdefiniowanie metody chronionej w interfejsie, który pozwala innej klasie w tym samym pakiecie na użycie tej metody na obiekcie implementującym, bez konieczności ujawniania tej metody, ma dla mnie sens, co nie może być przeznaczone do wywołania przez kogokolwiek innego niż członków pakietu do reszty świata.
Markus A.
4
@MarkusA. Ale interfejsy działają dwukierunkowo, tzn. Mogą być również implementowane przez klasy poza bieżącym pakietem (a następnie być może przekazywane jako argumenty do metod wewnątrz tego pakietu). W jaki sposób klasa spoza bieżącego pakietu mogłaby zaimplementować „chronione” metody jakiegoś publicznego interfejsu?
MartinStettner
8
@MartinStettner: Nie byłoby. To byłby punkt. Pakiet może mieć wiele niepowiązanych klas, które implementują interfejs i chcieć zagwarantować każdemu kodowi otrzymującemu odwołanie do tego typu interfejsu, że będzie się zachowywał w określony sposób. Taka gwarancja mogłaby być znacznie silniejsza, gdyby można było uniemożliwić zewnętrznemu kodowi twierdzenie, że implementuje interfejs, zachowując się w sposób sprzeczny z umową.
supercat
1
@MarkusA. podniosłeś dobry punkt, powinieneś być w stanie to osiągnąć dzięki systemowi modułów Java 9
Nir Alfasi
1
jeśli interfejs ma protectedmetodę, wszystkie klasy implementujące będą postrzegane jako podtyp interfejsu. a wszystkie te klasy MOGĄ mieć dostęp do metod chronionych. czy nie powoduje to, że protectedsłowo kluczowe w metodzie jest bezużyteczne? o ile nie mamy możliwości ograniczenia, kto implementuje ten interfejs, chronione słowo kluczowe w metodzie jest bezużyteczne. Popraw mnie, jeśli się mylę!
amarnath harish

Odpowiedzi:

67

Ponieważ interfejs ma oznaczać „to, co widać spoza klasy”. Nie ma sensu dodawać metod niepublicznych.

Raveline
źródło
16
Ale dlaczego nie mieć funkcji, które tylko członkowie tego samego pakietu co interfejs „widzą spoza klasy”? Miałem kilka przypadków użycia, w których chciałem tego.
Markus A.
5
@MarkusA. Zdaję sobie sprawę, że to późno, ale wtedy możesz zrobić wszystko abstract class, co jest w interfaceporządku, i określić dowolny dostęp. Przyznane to traci korzyści z wielu implementacji, które interfacesą dostępne w Javie, ale uczciwe ustanowienie kontraktu, który jest zgodny z ograniczeniami jakiegoś innego pakietu, byłoby niesprawdzalne i mylące, ponieważ praktycznie nie byłbyś w stanie uzyskać dostępu do metody własnej implementacji poza tym pakietem .
pickypg
10
@pickypg Jeśli jednak klasa, która implementowałaby interfejs, rozszerza już inną klasę, nie można uczynić jej rozszerzeniem innej klasy. Nie uznałbym tego za mylące dla interfejsu, który jest używany tylko w pakiecie.
Flamma
24
@Raveline, -1, nasuwa się pytanie "dlaczego interfejs ma oznaczać to, co możesz zobaczyć spoza klasy?" Java 8 już pozwala na umieszczanie treści metod w interfejsach, więc dlaczego nie pozwolić również na chronione metody abstrakcyjne?
Pacerier
8
Często czytam to wyjaśnienie, ale jest źle IMHO. Interfejs to rodzaj „standardowego protokołu wymiany danych”, bez względu na to, czy chodzi o OOP, komunikacyjne API czy sprzęt. Port USB w moim komputerze jest wyraźnie interfejsem publicznym. Ale piny na mojej płycie głównej, czyli za obudową zamykaną na klucz, zapewniające dostęp do opcjonalnych portów USB, są wyraźnie „chronionym” interfejsem. Następnie mamy układ BIOS - który jest również standardowym interfejsem, ale nigdy nie jest w żaden sposób upubliczniany, tylko kilka firm zna dokładne szczegóły prywatnie. Oczywiście interfejsy mogą mieć dowolną widoczność! Dlaczego nie w OOP?
Foo Bar
55

Chociaż często przytaczanym powodem jest to, że „interfejsy definiują publiczne API”, myślę, że jest to nadmierne uproszczenie. (I "pachnie" też logiką kolistą.)

Nie byłoby bez sensu posiadanie interfejsów zawierających mieszankę modyfikatorów dostępu; np. częściowo publiczne, a częściowo ograniczone do innych klas w tym samym pakiecie co interfejs. W rzeczywistości w niektórych przypadkach może to być przydatne w dół, IMO.

Właściwie uważam, że częścią rozumowania stojącego za upublicznieniem elementów interfejsu jest to, że upraszcza to język Java :

  • Niejawnie publiczne elementy składowe interfejsu są prostsze w obsłudze dla programistów. Ile razy widziałeś kod (klasy), w którym modyfikatory dostępu do metody były wybierane pozornie losowo? Wielu „zwykłych” programistów ma trudności ze zrozumieniem, jak najlepiej zarządzać granicami abstrakcji Javy 1 . Dodanie publicznego / chronionego / prywatnego pakietu do interfejsów sprawia, że ​​jest to jeszcze trudniejsze.

  • Niejawnie publiczne elementy interfejsu upraszczają specyfikację języka ... a tym samym zadanie dla autorów kompilatorów Java i osób, które implementują interfejsy API Reflection.

Myślenie, że „interfejsy definiują publiczne API” jest prawdopodobnie konsekwencją (lub cechą) decyzji dotyczącej projektu uproszczenia języka ... a nie na odwrót. Ale w rzeczywistości te dwa nurty myślenia prawdopodobnie rozwinęły się równolegle w umysłach projektantów Javy.

W każdym razie oficjalna odpowiedź na RFE w JDK-8179193 jasno pokazuje, że zespół projektowy Java zdecydował 2, że dopuszczenie protectedinterfejsów zwiększa złożoność i przynosi niewielkie realne korzyści. Uznanie dla @skomisa za znalezienie dowodów .

Dowody w RFE rozwiązują problem. To jest oficjalny powód, dla którego nie został dodany.


1 - Oczywiście programiści z najwyższej półki nie mają z tym trudności i mogą z zadowoleniem przyjąć bogatszą paletę funkcji kontroli dostępu. Ale co się stanie, gdy ich kod zostanie przekazany komuś innemu w celu utrzymania?

2 - Możesz nie zgadzać się z ich decyzją lub uzasadnieniem, ale to kwestia dyskusyjna.

Stephen C.
źródło
21

Muszę powiedzieć, że to pytanie zostało ponownie otwarte przez wprowadzenie domyślnych metod w Javie 8. Projekt, nad którym obecnie pracuję, jest podobny do podstawowego charakteru interfejsu i ma na celu oderwanie od implementacji.

Jest kilka przypadków, w których mógłbym drastycznie uprościć mój kod za pomocą metody „domyślnej ochrony”. Okazuje się, że to w rzeczywistości nie działa, ponieważ interfejsy nadal są zgodne z logiką Java 7. Zwykła metoda chroniona nie ma większego sensu z powodów wymienionych powyżej; ale jeśli domyślna metoda publiczna wymaga zasobu niskiego poziomu, który prawdopodobnie się nie zmieni i może być dostarczony za pomocą metody chronionej, wydaje mi się, że posiadanie pracy „domyślnej chronionej” nie tylko utrzymałoby czystszy kod, ale także chroniłoby przyszłych użytkowników przed przypadkowe nadużycia.

(To tragicznie nie zmienia faktu, że nadal muszę nadmiernie komplikować swój kod niepotrzebnymi streszczeniami, ale zamierzam złożyć prośbę o funkcję w Oracle).

Michael Oberlin
źródło
Zgadzam się w 100%. Klasy abstrakcyjne były rozsądną alternatywą przed wprowadzeniem metod domyślnych. Jednak ograniczenia, jakie nakładają na wielokrotne dziedziczenie, oznaczają, że nie są one doskonałe. Interfejsy z metodami domyślnymi są zwykle lepszą alternatywą w świecie> = JDK 1.8, ale ponieważ nie mogą przechowywać stanu, polegają na zdefiniowaniu innych abstrakcyjnych metod w celu ujawnienia stanu, co oznacza, że ​​stan jest upubliczniany, co nie zawsze jest tym, czym jest chcesz.
Ks. Jeremy Krieg
10

Ponieważ interfejsy definiują publiczne interfejsy API. Wszystko, co jest chronione, jest szczegółem wewnętrznym, który nie należy do interfejsu.

Możesz używać klas abstrakcyjnych z chronionymi metodami abstrakcyjnymi, ale interfejsy są ograniczone do metod publicznych i publicznych statycznych pól końcowych.

Sean Patrick Floyd
źródło
5
Napisałeś „ponieważ interfejsy definiują publiczne interfejsy API”. Więc jaki jest powód, dla którego interfejsy powinny definiować tylko publicinterfejsy API? Istnieje różnica między „szczegółem wewnętrznym” a „szczegółem implementacji”, protectedw Javie zdecydowanie nie jest to szczegół wewnętrzny, ponieważ jest to teraz publiczny interfejs publikowany dla każdego, kto może go podklasować, czyli w zasadzie dla całego świata.
Pacerier,
1
Już nie jest prawdą z domyślnymi metodami Java 8.
Mario Rossi
@MarioRossi I to już nie jest prawdą z prywatnymi metodami interfejsu Java 9.
skomisa
7

Być może dlatego, że jest to interfejs , czyli po to, aby powiedzieć klientom, co mogą zrobić z instancjami, a nie powiedzieć im, czego nie mogą zrobić.

Ingo
źródło
1
Nie rozumiem, dlaczego interfejs nie mógł również powiedzieć podklasom, co mogą zrobić bez ujawniania tej implementacji światu zewnętrznemu. To była decyzja projektowa, która została podjęta w czasie, gdy klasy abstrakcyjne mogły być używane jako doskonała alternatywa. Jednak wraz z pojawieniem się domyślnych metod w interfejsach, klasy abstrakcyjne są teraz niedoskonałą alternatywą.
Ks. Jeremy Krieg
6

Jestem głęboko przekonany , że interfejsy powinny umożliwiać metody chronione; kto powiedział, że interfejsy muszą być widoczne dla wszystkich na całym świecie? Jeśli chodzi o twój punkt widzenia, może to zmylić "zwykłych" (czytaj: niekompetentnych) programistów: tak wiele z OOP dotyczy właściwej struktury obiektów, klas, pakietów itp., Jeśli programista ma trudności z wykonaniem tego wszystkiego poprawnie, ma dużo większy problem. Java została stworzona do tego typu rzeczy.

IntelliData
źródło
To nie odpowiada na pytanie.
Stephen C
5

W kilku odpowiedziach stosuje się rozumowanie cykliczne, aby wyjaśnić, dlaczego metody interfejsu nie mogą być chronione: to dlatego, że muszą być publiczne, więc oczywiście nie można ich chronić!

To nic nie wyjaśnia, ale na szczęście ktoś kilka lat temu zgłosił prośbę o ulepszenie metod chronionych w interfejsach jako błąd JDK , co rzuca trochę światła na problem:

Metody chronione w interfejsach: udostępnianie w pakietach

Ponieważ modyfikatory są nieco ograniczone w Javie, sposób udostępniania metod w pakietach jest ograniczony do metod publicznych. Czasami upublicznienie metody jest niebezpieczne, ale musi to być spowodowane brakiem odpowiednich modyfikatorów. Moje rozwiązanie pokonuje to ograniczenie.

Specyfikacja języka Java nie zezwala obecnie na chroniony modyfikator metod interfejsu. Możemy wykorzystać ten fakt i zastosować chronione metody interfejsu dla tej nowej funkcji.

Jeśli metoda interfejsu jest oznaczona jako chroniona, a interfejs jest implementowany przez klasę w innym pakiecie, metoda nie musiałaby być publiczna, ale mogłaby być również prywatna lub przynajmniej chroniona pakietem. Metoda jest widoczna, jakakolwiek klasa deklaruje ją jako i dodatkowo widoczna w pakiecie źródłowym interfejsu (i pakietach podrzędnych?).

W ten sposób moglibyśmy udostępniać pewne metody w dobrze znanych pakietach.

A oto odpowiedź na to żądanie ulepszenia, które zostało zamknięte ze statusem Won't fix:

Ta propozycja jest próbą rozwiązania problemu w sposób, który zwiększa złożoność i powoduje szczególne przypadki, przy niewielkich korzyściach. Typowym sposobem rozwiązania tego problemu jest posiadanie klasy prywatnej, która implementuje interfejs publiczny. Metody implementacji są publiczne, ale znajdują się w klasie prywatnej, więc pozostają prywatne.

Alternatywą dostępną począwszy od Java 9 jest upublicznienie klas i metod, ale w module, który ma kwalifikowany eksport do określonych modułów „zaprzyjaźnionych”, zamiast być eksportowanym do ogółu społeczeństwa.

Tak więc autorytatywne wnioski z tego raportu o błędzie to:

  • Obecna sytuacja nie ulegnie zmianie; interfejsy prawdopodobnie nigdy nie będą obsługiwać protectedmetod.
  • Uzasadnieniem dla braku obsługi protectedmetod w interfejsach jest to, że „ zwiększa złożoność i specjalne przypadki dla niewielkiego rzeczywistego zysku ”.
  • Od wersji Java 9 istnieje alternatywne podejście do zapewniania dostępu do metod na poziomie pakietu. Użyj Java Platform Module System (JPMS), aby „ upublicznić klasy i metody, ale w module, który ma kwalifikowany eksport do określonych modułów„ zaprzyjaźnionych ”, zamiast być eksportowanym do ogółu społeczeństwa ”.
skomisa
źródło
4

Ponieważ klasa implementująca musi implementować WSZYSTKIE metody zadeklarowane w Twoim interfejsie, co by się stało, gdyby klasa implementująca znajdowała się w innym pakiecie?

dm76
źródło
2

Interfejs Jeśli chcesz użyć czegoś takiego, jak opisałeś, kontynuuj z klasami abstrakcyjnymi lub zagnieżdżonymi interfejsami.

Fragment ze stylu kodu o zmiennych interfejsowych, ale nadal ma zastosowanie do metod:

Zmienne interfejsu są niejawnie publiczne, ponieważ interfejsy mają zapewniać interfejs programowania aplikacji (API), który jest w pełni dostępny dla programistów języka Java do odwoływania się i implementacji we własnych aplikacjach. Ponieważ interfejs może być używany w pakietach Java, które różnią się od ich własnych, publiczna widoczność zapewnia, że ​​kod programu ma dostęp do zmiennej.
siwy
źródło
2

Deklarowanie wewnętrznych podinterfejsów jest dobrą praktyką, ale technicznie nie można deklarować swoich metod wewnętrznych, tak jak protectedw interfejsie w Javie.

Oczywiście możesz stworzyć inny interfejs do użytku wewnętrznego, który rozszerza interfejs publiczny:

package yourpackage;

public interface PublicInterface {

    public void doThing1();

    public void doThing2();

    public void doThing3();

}

package yourpackage;

interface InternalInterface extends PublicInterface {

    void doAnyInternalThing1();

    void doAnyInternalThing2();

}

Możesz użyć InternalInterfaceinterfejsu wewnątrz pakietu, ale powinieneś zaakceptować dowolny podtyp PublicInterface(w metodach publicznych):

package yourpackage;

public class SomeClass {

    public void someMethod(PublicInterface param) {
        if (param instanceof InternalInterface) {
            // run the optimized code
        } else {
            // run the general code
        }
    }

}

Poza pakietem użytkownicy mogą korzystać PublicInterfacebez problemów.

Zwykle programiści tworzą klasy abstrakcyjne w podobnych sytuacjach. Jednak w tym przypadku tracimy korzyści wynikające z wielokrotnego dziedziczenia.

Dávid Horváth
źródło
1
Poza pakietem użytkownicy mogą również używać YourPublicInterface.Internal. Wszystko w interfejsie, w tym interfejsy zagnieżdżone, jest publiczne, niezależnie od obecności lub braku publicsłowa kluczowego.
Greg Roelofs
1

Jedynym scenariuszem, w którym miałoby to sens, jest ograniczenie widoczności do tego samego pakietu. Wszystkie inne zastosowania protectednie mają zastosowania. W szczególności protectedmetody są często używane w celu zapewnienia dostępu do niektórych szczegółów implementacji niższego poziomu dla potomków. Ale deklarowanie tego w interfejsie nie ma sensu, ponieważ nie ma implementacji niższego poziomu do ujawnienia.

I nawet scenariusz pakietu nie jest tak naprawdę tym, o co chodzi w interfejsach.

Aby osiągnąć to, czego prawdopodobnie chcesz, potrzebujesz dwóch interfejsów, jednego do użytku wewnętrznego i jednego, który udostępniasz w publicznym API. (Z wewnętrzną możliwie, ale niekoniecznie rozszerzającą publiczną.) Lub, jak wskazywali inni, abstrakcyjna superklasa.

biziclop
źródło
Abstrakcyjna nadklasa może uniemożliwić jej wyprowadzenie przez typy spoza jej pakietu. Ktoś, kto otrzyma referencję takiego typu nadklasy i ufa autorowi pakietu, może być pewny, że obiekt będzie zachowywał się zgodnie z założeniami. Jeśli pakiet ma wiele klas, które chcą ujawnić jakąś wspólną funkcjonalność, ale nie pasują do odpowiedniej hierarchii (np. Jedna implementuje funkcje X i Y, jedna Y i Z oraz jedna X i Z), byłoby pomocne, gdyby funkcjonalność wykorzystująca interfejsy, choć wciąż obiecuje, że wystąpienia, do których odnoszą się typy interfejsów, będą „autentyczne”.
supercat
0

Metody chronione są zawsze dostępne dla podklasy tylko wtedy, gdy podklasa rozszerza klasę bazową.

W przypadku interfejsu podklasa nigdy nie rozszerza interfejsu. Implementuje interfejs.

Metody chronione są dostępne za pośrednictwem rozszerzenia, a nie narzędzia .

Aftaab Siddiki
źródło
jaka różnica jakie słowo kluczowe, to nie ma znaczenia.
Alex78191
0

Interfejsy mają na celu ujawnienie metod światu zewnętrznemu . Zatem metody te są z natury publiczne. Jeśli jednak chcesz wprowadzić abstrakcję w ramach tej samej rodziny klas , możesz stworzyć inny poziom abstrakcji między Twoim interfejsem a klasą implementacji, czyli klasę abstrakcyjną. Przykład przedstawiono poniżej.

public interface MyInterface {
    public void publicMethod(); // needs to be public
}

public abstract class MyAbstractClass implements MyInterface {
    @Override
    public void publicMethod() {
        protectedMethod(); // you can call protected method here
        // do other stuff
    }
    protected abstract void protectedMethod(); // can be protected
}

public class MyClass extends MyAbstractClass {
    @Override
    protected void protectedMethod() {
        // implement protected method here, without exposing it as public
    }
}
Stefanos Kargas
źródło
2
Jednak w interfejsach dozwolone są metody prywatne, których nikt nie zobaczy.
Alex78191
java ma domyślne metody w interfejsach, to znaczy interfejs jest podpórką do ominięcia wielokrotnego dziedziczenia klas abstrakcyjnych. Niech więc jest dozwolone wielokrotne dziedziczenie klas abstrakcyjnych. Konflikty metod domyślnych nie stały się problemem.
Alex78191
Masz rację. Od wersji Java 9 w interfejsach dozwolone są prywatne metody. Nie mogą być abstrakcyjne i są implementowane i używane w interfejsie, głównie za pomocą innych metod domyślnych lub statycznych (jeśli same są statyczne).
Stefanos Kargas
1
Ta odpowiedź po prostu ignoruje zadane pytanie: dlaczego interfejsy nie mogą mieć chronionych metod? Metody chronione nadal „ujawniałyby metody światu zewnętrznemu” , a twierdzenie, że „metody te są z natury publiczne”, jest po prostu fałszywe. Są publiczne, ponieważ język został zaprojektowany w ten sposób, ale mógł pozwolić na chronione metody w interfejsach. PO po prostu pyta dlaczego.
skomisa