Dlaczego Java nie pozwala prywatnym członkom w interfejsie? Czy jest jakiś szczególny powód?
Dlaczego Java nie pozwala prywatnym członkom w interfejsie? Czy jest jakiś szczególny powód?
Odpowiedzi:
Ze specyfikacji języka Java (Kontrola dostępu) :
„Język programowania Java zapewnia mechanizmy kontroli dostępu, aby użytkownicy pakietu lub klasy nie byli uzależnieni od niepotrzebnych szczegółów implementacji tego pakietu lub klasy”.
Kontrola dostępu polega na ukrywaniu szczegółów implementacji. Interfejs nie ma implementacji do ukrycia.
W Javie 9 możliwe są prywatne metody w interfejsach.
Zespół kompilatorów javac z przyjemnością informuje o dostępności obsługi kompilatorów dla metod prywatnych w interfejsach począwszy od wersji 9 b54 JDK.
Prywatne metody interfejsu są częścią Java 9 jako część JEP-213 . Ponieważ interfejsy w Javie 8 mogą mieć metody domyślne, metody prywatne zezwalają na wiele domyślnych metod korzystania z udostępnionej metody prywatnej.
Od wersji Java 8 interfejsy mogą mieć metody domyślne, a od wersji Java 9 interfejs może mieć metody prywatne, do których dostęp jest możliwy tylko przy użyciu metod domyślnych w tym samym interfejsie.
Interfejs jest używany do opisu API, które jest dostarczane przez dowolną klasę implementującą interfejs. Ponieważ interfejs z definicji nie ma stanu, nie ma potrzeby deklarowania w nim elementów pola.
Nie byłoby sposobu na zaimplementowanie takiego interfejsu. Odpowiedź na zadane przeze mnie pytanie zdecydowanie sugeruje, że niemożliwe byłoby (bez radykalnej zmiany reguł) zaimplementowanie interfejsu z metodami prywatnymi - to pozostawia otwarte pytanie, dlaczego metody chronione i pakietowe nie są dozwolone.
class OuterClass
{
void run ( MyInterface x )
{
x . publicMethod ( ) ; // why not?
x . protectedMethod ( ) ; // why not?
x . packagePrivateMethod ( ) ; // why not?
x . privateMethod ( ) ; // why not?
}
interface MyInterface
{
public abstract void publicMethod ( ) ; // OK
protected abstract void protectedMethod ( ) ; // why not?
abstract void packagePrivateMethod ( ) ; // in interface default is public, but why not package private
private void privateMethod ( ) ; // impossible to implement
}
class MyImpl implements MyInterface
{
public void publicMethod ( ) { } // ok
protected void protectedMethod ( ) { } // no sweat
void packagePrivateMethod ( ) { } // no sweat
private void privateMethod ( ) { } // not happening
}
}
Poniższy kod powinien przynieść oczekiwany efekt. Chociaż wszystkie metody są publiczne, tylko metoda publiczna jest faktycznie publiczna. chroniona metoda jest skutecznie chroniona. packagePrivateMethod jest efektywnie packagePrivate. privateMethod jest faktycznie prywatny.
class WorkAround
{
void run ( MyPrivateInterface x )
{
x . publicMethod ( ) ;
x . protectedMethod ( ) ;
x . packagePrivateMethod ( ) ;
x . privateMethod ( ) ;
}
public interface MyPublicInterface { void publicMethod ( ) ; }
protected interface MyProtectedInterface extends MyPublicInterface { void protectedMethod ( ) ; }
interface MyPackagePrivateInterface extends MyProtectedInterface { void packagePrivateMethod ( ) ; }
private interface MyPrivateInterface extends MyPackagePrivateInterface { void privateMethod ( ) ; }
}
Zgodnie z Javajęzykiem programowania zakres private membersjest ograniczony do tego, classw którym jest zadeklarowany i można uzyskać do niego dostęp tylko za pomocą metod class. Ale intefacenie ma treści metody, dlatego nie ma potrzeby deklarowania prywatnych członków wewnątrz pliku interface.
Java zezwala na prywatne metody w interfejsie w Javie 9 . Te metody domyślne zostały wprowadzone w Javie 8. Możliwe jest, że wiele metod domyślne chcesz podzielić się jakiś kod, to kod ten może zostać przeniesiony do prywatnej metody bez narażania go do świata zewnętrznego. Ten błąd został naprawiony i począwszy od JDK 9 build 54, przywrócono obsługę kompilatorów dla prywatnych metod interfejsu.
public interface IData{
default void processData(int data) {
validate(data);
// do some work with it
}
default void consumeData(int data) {
validate(data);
// do some work with it
}
private void validate(int data) {
// validate data
}
}
To dlatego, że byłyby bezużyteczne.
Nie byłoby sposobu na wywołanie metody prywatnej.
Prywatni członkowie są szczegółem implementacji. Interfejs dotyczy roli publicznej, którą może przyjąć klasa.
pola prywatne nie byłyby całkowicie bezużyteczne, ponieważ inne pola i klasy wewnętrzne miałyby do nich dostęp.
Jednak metody prywatne nie mogły zostać zaimplementowane, nawet w klasach zagnieżdżonych, co czyni je prawie bezużytecznymi. Możesz je przeczytać za pomocą odbicia, ale to raczej skrajny przypadek.
Składowe klasy, które są zadeklarowane jako prywatne, nie są dziedziczone przez podklasy tej klasy. Tylko elementy członkowskie klasy, które są zadeklarowane jako chronione lub publiczne, są dziedziczone przez podklasy zadeklarowane w pakiecie innym niż ten, w którym klasa jest zadeklarowana.
Źródło
Więc nie masz żadnych metod pracy w interfejsie, które mogłyby pracować z tym prywatnym niedziedziczalnym polem, więc dlaczego miałoby ono istnieć?
Tak, nie mogę tego zrobić. Dla wszystkich komentujących, dlaczego nie powinien:
Wyobraź sobie, że mam klasę A, która wykorzystuje interfejs I. Klasa B rozszerza klasę A, dlatego też dziedziczę wszystkie metody interfejsu w A.
Teraz wyobraź sobie, że chcę prywatną metodę w klasie A, ale chcę, aby była zdefiniowana w umowie również dla innych klas (może klasa C, która niekoniecznie rozszerza klasę B lub A).
Być może dla metody „inicjalizacji”, której chcę dla wszystkich klas używających interfejsu I. Ale oczywiście nie chcę, aby metoda inicjalizacji była publiczna ... ponieważ powinna być używana tylko raz, lub jeśli klasa uzna to za konieczne, a nie tylko dlatego, że chcesz użyć jej wszystkich chcąc nie chcąc.
Jedynym rozwiązaniem jest obejście problemu lub po prostu wymuszenie metody init w samych klasach bez interfejsu.
Na pewno nie rozumiem tego powodu, ale czasami może się przydać. Wyraźnie Oracle zgadza się, ponieważ zezwala na prywatne metody interfejsu w JDK 9.
To, co zrobiłem, w każdym razie dla mojego, to umieszczenie prostej zmiennej boolowskiej, w ten sposób metoda interfejsu (która powinna być prywatna) może zostać oznaczona jako prawda (zainicjowana = prawda) po jednorazowym ustawieniu. Po ponownym wywołaniu metoda po prostu nic nie robi. W ten sposób metoda interfejsu może zostać zaimplementowana jako publiczna, ale ponieważ konstruktor (mojej klasy) najpierw wywołuje metodę, ustawia to zmienną na true, więc nie można jej ponownie wywołać.
W przeciwnym razie musiałbyś wypróbować inne obejście, jeśli chcesz, aby tylko wewnętrzne funkcje klasy używały jej ... być może sama metoda ustawia flagę, gdy jej używa. Gdy flaga ma wartość false, metoda nic nie robi (byłoby tak, gdy ktoś wywoła ją spoza klasy). Jednak gdy wywołują ją metody klasy, szybko ustawiają flagę na wartość true, następnie wywołują metodę, a następnie ustawiają flagę na wartość false ??
W końcu coś w rodzaju wyciszenia. Prawdopodobnie na razie lepiej po prostu umieścić klasę prywatną w samej klasie i całkowicie odciąć interfejs.