Pytanie w sprawie interface, java, methods – Opcjonalne metody w interfejsie Java

101

Z mojego zrozumienia, jeśli zaimplementujesz interfejs w Javie, metody określone w tym interfejsie muszą być używane przez podklasy implementujące wspomniany interfejs.

Zauważyłem, że w niektórych interfejsach, takich jak interfejs Collection, istnieją metody, które są komentowane jako opcjonalne, ale co to właściwie znaczy? Trochę mnie to rzuciło, ponieważ myślałem, że wszystkie metody określone w interfejsie będą wymagane?

Do jakich metod się odwołujesz? Nie mogę go znaleźć w JavaDoc ani w kodzie źródłowym dcpomero

Twoja odpowiedź

12   odpowiedzi
16

że implementacja metody może rzucić wyjątek, ale i tak musi zostać zaimplementowana. Jak określonow dokumentach:

Niektóre implementacje kolekcji mają ograniczenia dotyczące elementów, które mogą zawierać. Na przykład niektóre implementacje zabraniają elementów pustych, a niektóre mają ograniczenia dotyczące typów ich elementów. Próba dodania elementu niekwalifikującego się powoduje zgłoszenie niesprawdzonego wyjątku, zwykle NullPointerException lub ClassCastException. Próba zapytania o obecność niekwalifikującego się elementu może spowodować zgłoszenie wyjątku lub może po prostu zwrócić false; niektóre implementacje będą wykazywać poprzednie zachowanie, a inne będą pokazywać to drugie. Mówiąc bardziej ogólnie, próba wykonania operacji na niekwalifikowalnym elemencie, którego zakończenie nie spowodowałoby wstawienia niekwalifikowalnego elementu do kolekcji, może spowodować wyjątek lub może się udać, w zależności od opcji wdrożenia. Takie wyjątki są oznaczone jako „opcjonalne” w specyfikacji tego interfejsu.

Nigdy tak naprawdę nie rozumiałem, co javadocs oznaczało jako opcjonalne. Wierzę, że miały na myśli to, co powiedziałeś. Jednak większość metod jest opcjonalna w tym standardzie:new Runnable ( ) { @ Override public void run ( ) { throw new UnsupportedOperationException ( ) ; } }; emory
Wydaje się, że nie dotyczy tometody opcjonalne, ale raczej na przykładadd((T)null) może być ważny w jednym przypadku, alenie inne. Oznacza to, że mówi o opcjonalnych wyjątkach / zachowaniu idla argumentów („ograniczenia elementów” ... „niekwalifikowalny element” ... „wyjątki oznaczone jako opcjonalne”) i nie dotyczymetody opcjonalne. user166390
0

warto zauważyć, że od Javy 8 dodaje się domyślne metody do interfejsówjest w rzeczywistości wykonalny. Thedefault Słowo kluczowe umieszczone w sygnaturze metody interfejsu spowoduje, że klasa będzie miała opcję nadpisania metody, ale jej nie wymaga.

205

Język Java wymaga, aby każda metoda w interfejsie była implementowana przez każdą implementację tego interfejsu. Kropka.Nie ma wyjątków od tej zasady. Powiedzieć „Kolekcje są wyjątkiem” sugeruje bardzo niejasne zrozumienie tego, co naprawdę się tutaj dzieje.

Ważne jest uświadomienie sobie, że istnieją dwa poziomy zgodności z interfejsem:

Co może sprawdzić język Java. To prawie wszystko sprowadza się do: jesttrochę implementacja dla każdej z metod?

Faktycznie realizacja umowy. Czy implementacja robi to, co mówi dokumentacja w interfejsie?

Dobrze napisane interfejsy będą zawierały dokumentację wyjaśniającą dokładnie, czego oczekuje się od implementacji. Twój kompilator nie może tego sprawdzić. Musisz przeczytać dokumenty i robić to, co mówią. Jeśli nie zrobisz tego, co mówi umowa, będziesz miał implementację interfejsu aż dokompilator dotyczy, ale będzie to wadliwe / nieprawidłowe wdrożenie.

Podczas projektowania interfejsu API kolekcji Joshua Bloch zdecydował, że zamiast bardzo drobnoziarnistych interfejsów rozróżniających różne warianty kolekcji (np. Czytelny, zapisywalny, losowy itp.) Miałby tylko bardzo zgrubny zestaw interfejsów, głównieCollection, List, Set iMap, a następnie udokumentuj pewne operacje jako „opcjonalne”. Miało to zapobiec wybuchowi kombinatorycznemu, który wynikałby z drobnoziarnistych interfejsów. OdCzęsto zadawane pytania dotyczące projektowania interfejsu API kolekcji Java:

Aby zilustrować problem w szczegółowy sposób, załóżmy, że chcesz dodać pojęcie modyfikowalności do hierarchii. Potrzebujesz czterech nowych interfejsów: ModifiableCollection, ModifiableSet, ModifiableList i ModifiableMap. To, co wcześniej było prostą hierarchią, jest teraz chaotyczną heterarchią. Potrzebny jest również nowy interfejs Iteratora do użycia z niemodyfikowalnymi kolekcjami, który nie zawiera operacji usuwania. Czy możesz teraz zrezygnować z UnsupportedOperationException? Niestety nie.

Rozważ tablice. Realizują większość operacji listy, ale nie usuwają i nie dodają. Są to listy „o stałym rozmiarze”. Jeśli chcesz uchwycić to pojęcie w hierarchii, musisz dodać dwa nowe interfejsy: VariableSizeList i VariableSizeMap. Nie musisz dodawać VariableSizeCollection i VariableSizeSet, ponieważ byłyby one identyczne z ModifiableCollection i ModifiableSet, ale możesz mimo to dodać je dla dobra spójności. Ponadto potrzebujesz nowej odmiany ListIterator, która nie obsługuje operacji dodawania i usuwania, aby przejść do niemodyfikowalnej listy. Teraz mamy do dziesięciu lub dwunastu interfejsów, plus dwa nowe interfejsy Iteratora, zamiast naszych oryginalnych czterech. Skończyliśmy? Nie.

Rozważ logi (takie jak dzienniki błędów, dzienniki kontroli i dzienniki dla możliwych do odzyskania obiektów danych). Są to naturalne sekwencje tylko do dodawania, które obsługują wszystkie operacje listy z wyjątkiem usuwania i ustawiania (zastępowania). Wymagają nowego interfejsu rdzenia i nowego iteratora.

A co z niezmiennymi kolekcjami, w przeciwieństwie do niemodyfikowalnych? (tj. Kolekcje, które nie mogą zostać zmienione przez klienta I nigdy nie zmienią się z innego powodu). Wielu twierdzi, że jest to najważniejsza różnica, ponieważ pozwala wielu wątkom na równoczesny dostęp do kolekcji bez potrzeby synchronizacji. Dodanie tej obsługi do hierarchii typów wymaga czterech dodatkowych interfejsów.

Teraz mamy około dwudziestu interfejsów i pięć iteratorów, i jest prawie pewne, że wciąż istnieją kolekcje powstające w praktyce, które nie pasują do żadnego z interfejsów. Na przykład widoki kolekcji zwracane przez Map są naturalnymi kolekcjami przeznaczonymi wyłącznie do usuwania. Istnieją także kolekcje, które odrzucają pewne elementy na podstawie ich wartości, więc nadal nie uniknęliśmy wyjątków czasu wykonywania.

Kiedy wszystko zostało powiedziane i zrobione, czuliśmy, że jest to rozsądny kompromis inżynieryjny, aby uniknąć całego problemu, dostarczając bardzo mały zestaw podstawowych interfejsów, które mogą rzucać wyjątek czasu wykonywania.

Gdy metody w interfejsie API zbiorów są udokumentowane jako „operacje opcjonalne”, nie oznacza to, że można po prostu pozostawić implementację metody w implementacji, ani nie oznacza, że ​​można użyć pustej treści metody (po pierwsze, wiele z nich muszą zwrócić wynik). Oznacza to raczej, że ważny wybór wdrożenia (taki, który nadal jest zgodny z umową) polega na rzuceniuUnsupportedOperationException.

Zauważ, że ponieważUnsupportedOperationException jestRuntimeException możesz go rzucić z dowolnej implementacji metody, jeśli chodzi o kompilator. Na przykład, możesz wyrzucić go z implementacjiCollection.size(). Jednak taka implementacja naruszyłaby umowę jako dokumentacjęCollection.size() nie mówi, że jest to dozwolone.

Na marginesie: podejście zastosowane w interfejsie API kolekcji Java jest nieco kontrowersyjne (prawdopodobnie mniej niż teraz, gdy zostało po raz pierwszy wprowadzone). W idealnym świecie interfejsy byłybynie mieć opcjonalne operacje, a zamiast tego używane byłyby drobnoziarniste interfejsy. Problem polega na tym, że Java nie obsługuje ani typów strukturalnych, ani typów skrzyżowań, dlatego próba robienia rzeczy „w prawidłowy sposób” staje się niezwykle niewygodna w przypadku kolekcji.

@ Andrews, ponieważ w Javie 8remove otrzymał domyślną implementację. Jeśli go nie zaimplementujesz, twoja klasa otrzyma domyślną implementację. Pozostałe dwie metody, o których wspomniałeś, nie mają domyślnych implementacji. Laurence Gonsalves
„Język Java wymaga, aby każda metoda w interfejsie była implementowana przez każdą implementację tego interfejsu. Okres. Nie ma wyjątków od tej reguły.” Z wyjątkiem ... kiedy są. :-) Interfejsy Java 8 mogą określać domyślną implementację metody, więc w Javie 8 ... NIE jest prawdą, że każda metoda w interfejsie musi być REALIZOWANA przez każdą implementację interfejsu, przynajmniej nie w tym sensie, że musisz zakodować implementację w klasie conrete. DaBlick
Dzięki Laurence, że to wyjaśnia. Andrew S
Przeprosiny. Nie starałem się być pedantycznie krytyczny tak bardzo, aby zwrócić uwagę na funkcję Java 8, która może być odpowiednia dla oryginalnego posta. W Javie 8 masz teraz możliwość implementacji, które nie są zakodowane w DOWOLNEJ superklasie ani podklasie. To (IMHO) otwiera nowy świat wzorców projektowych, w tym niektóre, które mogą być odpowiednie w przypadkach, w których zakaz wielokrotnego dziedziczenia mógł stanowić pewne wyzwanie. Myślę, że to zrodzi nowy zestaw bardzo przydatnych wzorców projektowych. DaBlick
3

że brakuje jednej odpowiedzi. Mówię o „domyślnych metodach” interfejsów. Na przykład, wyobraźmy sobie, że masz klasę do zamykania czegokolwiek (jak destruktor lub coś). Powiedzmy, że powinien mieć 3 metody. Nazwijmy je „doFirst ()”, „doLast ()” i „onClose ()”.

Mówimy więc, że chcemy, aby każdy obiekt tego typu przynajmniej realizował „onClose ()”, ale drugi jest opcjonalny.

Możesz to sobie uświadomić, używając „Domyślnych metod” interfejsów. Wiem, że większość czasu neguje powód interfejsu, ale jeśli projektujesz framework, może to być przydatne.

Więc jeśli chcesz to zrealizować w ten sposób, wyglądałoby to następująco

<code>public interface Closer {
    default void doFirst() {
        System.out.print("first ... ");
    }
    void onClose();
    default void doLast() {
        System.out.println("and finally!");
    }
}
</code>

Co teraz by się stało, gdybyś na przykład zaimplementował go w klasie o nazwie „Test”, kompilator byłby w porządku z następującymi elementami:

<code>public class TestCloser implements Closer {
    @Override
    public void onClose() {
        System.out.print("closing ... ");
    }
}
</code>

z wyjściem:

<code>first ... closing ... and finally!
</code>

lub

<code>public class TestCloser implements Closer {
    @Override
    public void onClose() {
        System.out.print("closing ... ");
    }

    @Override
    public void doLast() {
        System.out.println("done!");
    }
}
</code>

z wyjściem:

<code>first ... closing ... done!
</code>

Wszystkie kombinacje są możliwe. Wszystko, co zawiera „domyślne”, może zostać zaimplementowane, ale nie może, jednak wszystko bez konieczności musi zostać zaimplementowane.

Mam nadzieję, że nie jest w pełni błędne, że teraz odpowiadam.

Miłego dnia wszyscy!

[edit1]: Uwaga: działa tylko w Javie 8.

dzięki za pomoc Thorben AndroidLover
Tak, przepraszam, zapomniałem o tym wspomnieć ... Powinien być teraz edytowany. Thorben Kuck
To działa tylko na Javie 8? * AndroidLover
0

Samouczek Oracle dotyczący kolekcji Java:

Aby utrzymać liczbę interfejsów do zbierania podstawowych danych, platforma Java nie zapewnia oddzielnych interfejsów dla każdego wariantu każdego typu kolekcji. (Takie warianty mogą obejmować niezmienne, o stałym rozmiarze i tylko do dodawania.) Zamiast tego operacje modyfikacji w każdym interfejsie są oznaczone jako opcjonalne - dana implementacja może zdecydować się nie obsługiwać wszystkich operacji. Jeśli zostanie wywołana nieobsługiwana operacja, kolekcja rzucaUnsupportedOperationException. Wdrożenia są odpowiedzialne za dokumentowanie, które z opcjonalnych operacji obsługują. Wszystkie implementacje ogólnego przeznaczenia platformy Java obsługują wszystkie opcjonalne operacje.

0

więc wdrożenie opcjonalnych metod było konieczne, ponieważ nie chciałem implementować każdej metody dla każdego wywołania zwrotnego.

Zamiast używać interfejsu, użyłem klasy z pustą implementacją, takiej jak:

<code>public class MyCallBack{
    public void didResponseCameBack(String response){}
}
</code>

I możesz ustawić zmienną członkowską CallBack w ten sposób,

<code>c.setCallBack(new MyCallBack() {
    public void didResponseCameBack(String response) {
        //your implementation here
    }
});
</code>

to nazwij to w ten sposób.

<code>if(mMyCallBack != null) {
    mMyCallBack.didResponseCameBack(response);
}
</code>

W ten sposób nie musisz martwić się o implementację wszystkich metod na wywołanie zwrotne, ale nadpisuj tylko te, których potrzebujesz.

25

jsu - wszystkie metody muszą zostać zaimplementowane.

jednak, jeśli pomyślimy o metodzie, której implementacja jest prostym rzutem wyjątku jako „niezaimplementowany” (jak niektóre metody wCollection interfejs), a następnieCollection interfejs jest w tym przypadku wyjątkiem, a nie zwykłym przypadkiem.Zazwyczajklasa implementująca powinna (i będzie) implementować wszystkie metody.

„Opcjonalne” w kolekcji oznacza, że ​​klasa implementująca nie musi „implementować” (zgodnie z powyższą terminologią) i po prostu rzuciNotSupportedException).

Dobry przykład-add() metoda dla niezmiennych kolekcji - beton po prostu wdroży metodę, która nie robi nic poza rzucaniemNotSupportedException

W przypadkuCollection ma to na celu zapobieganie bałaganiarskim drzewom dziedziczenia, które sprawią, że programiści będą nieszczęśliwi - ale dlawiększość przypadki, ten paradygmat nie jest zalecany i należy go unikać, jeśli to możliwe.

Aktualizacja:

Od java 8, ametoda domyślna został wprowadzony.

Oznacza to, że interfejs może zdefiniować metodę - w tym jej implementację.
Zostało to dodane w celu umożliwienia dodawania funkcjonalności do interfejsów, przy jednoczesnym wspieraniu wstecznej kompatybilności dla fragmentów kodu, które nie potrzebują nowej funkcjonalności.

Zauważ, że metoda jest nadal implementowana przez wszystkie klasy, które ją deklarują, ale używając definicji interfejsu.

@pst: Iuwierzyć to, co myśleli projektanci, wdrażając go w pierwszej kolejności, ale nie mam sposobu, aby to wiedzieć. jamyśleć każde inne podejście spowodowałoby po prostu bałagan, ale znowu - mogłoby być błędne. Punkt, który chciałem tutaj pokazać, jest następujący: ten przykład jest wyjątkiem, a nie zwykłym - i choć czasami może być przydatny - w przypadku ogólnym - należy go unikać, jeśli to możliwe. amit
„Opcjonalne” w kolekcji oznacza, że ​​klasa implementująca nie musi jej implementować. ”- jest to zwykłe fałszywe. Przez„ nie musi implementować ”masz na myśli coś innego. djechlin
Zamiast „nie robić bałaganu”, myślę, że jest to bardziej „tak właśnie jest”. user166390
„nie musi go implementować (prawdopodobnie stworzy metodę, która rzuca ...)”. Żejest wdrożenie metody. user207421
Ponieważ niestety była to zaakceptowana odpowiedź, proponuję ją przepisać. The 'Zazwyczajklasa implementująca powinna (i będzie) implementować wszystkie metody ”jest myląca, jakEJP już wskazane. Alberto
17

dy w tym interfejsiemusi zostać wdrożone, ale klasy wdrażające mogą pozostawić je niezrealizowane, tzn. puste. Jako wymyślony przykład

<code>interface Foo {
  void doSomething();
  void doSomethingElse();
}

class MyClass implements Foo {
  public void doSomething() {
     /* All of my code goes here */
  }

  public void doSomethingElse() {
    // I leave this unimplemented
  }
}
</code>

Teraz odszedłemdoSomethingElse() niezrealizowane, pozostawiając za darmo moje podklasy do wdrożenia. To jest opcjonalne.

<code>class SubClass extends MyClass {
    @Override
    public void doSomethingElse() {
      // Here's my implementation. 
    }
}
</code>

Jeśli jednak mówisz o interfejsach Collection, jak powiedzieli inni, są one wyjątkiem. Jeśli pewne metody pozostaną niezrealizowane, a ty je nazwiesz, mogą rzucićUnsupportedOperationException wyjątki.

Mogę cię pocałować mój przyjacielu. Micro
4

Jeśli przejdziemy przez kodAbstractCollection.java w grepCode, który jest klasą przodków dla wszystkich implementacji kolekcji, pomoże nam zrozumieć znaczenie metod opcjonalnych. Oto kod metody add (e) w klasie AbstractCollection. add (e) metoda jest opcjonalna zgodnie zkolekcja berło

<code>public boolean  add(E e) {

        throw new UnsupportedOperationException();
    } 
</code>

Metoda opcjonalna oznacza, że ​​jest już zaimplementowana w klasach przodków i zgłasza wyjątek UnsupportedOperationException po wywołaniu. Jeśli chcemy, aby nasza kolekcja była modyfikowalna, powinniśmy zastąpićopcjonalny metody w interfejsie zbierania danych.

4

ale jest teraz bardziej dopracowana.

Po pierwsze, te stwierdzenia z zaakceptowanej odpowiedzi pozostają poprawne:

interfejsy mają na celu określenie ich ukrytych zachowań w umowie (oświadczenie o regułach zachowania, które klasy wykonawcze muszą przestrzegać, aby mogły być uznane za ważne)istnieje rozróżnienie między umową (zasadami) a wdrożeniem (programowe kodowanie zasad)metody określone w interfejsie MUSZĄ ZAWSZE być implementowane (w pewnym momencie)

Jaki jest nowy niuans w Javie 8? Kiedy mówisz o„Metody opcjonalne” dowolne z poniższych są teraz trafne:

1. Metoda, której realizacja jest umownie opcjonalna

„Trzecie stwierdzenie” mówi, że abstrakcyjne metody interfejsu muszą być zawsze implementowane, co pozostaje prawdziwe w Javie 8+. Jednak, jak w Java Collections Framework, możliwe jest opisanie niektórych abstrakcyjnych metod interfejsu jako „opcjonalnych” w umowie.

W takim przypadku autor implementujący interfejs może zrezygnować z implementacji metody. Kompilator będzie jednak nalegał na implementację, więc autor używa tego kodu do wszelkich opcjonalnych metod, które nie są potrzebne w konkretnej klasie implementacji:

<code>public SomeReturnType optionalInterfaceMethodA(...) {
    throw new UnsupportedOperationException();
}
</code>

W Javie 7 i wcześniejszych był to naprawdę jedyny rodzaj „opcjonalnej metody”, tj. Metoda, która, jeśli nie została zaimplementowana, rzuciła wyjątek UnsupportedOperationException. To zachowanie jest koniecznie określone przez umowę interfejsu (np. Opcjonalne metody interfejsu Java Collections Framework).

2. Domyślna metoda, której ponowne wdrożenie jest opcjonalne

Java 8 wprowadziła koncepcjędomyślne metody. Są to metody, których implementacja może być i jest zapewniana przez samą definicję interfejsu. Zwykle możliwe jest tylko podanie metod domyślnych, gdy treść metody może być zapisana przy użyciu innych metod interfejsu (np. „Prymitywów”) i kiedythis może oznaczać „ten obiekt, którego klasa zaimplementowała ten interfejs”.

Domyślna metoda musi spełniać umowę interfejsu (tak jak każda inna metoda implementacji interfejsu). Dlatego określenie implementacji metody interfejsu w klasie implementacyjnej zależy od uznania autora (o ile zachowanie jest odpowiednie do jego celu).

W tym nowym środowisku Java Collections Frameworkmożliwe przepisany jako:

<code>public interface List<E> {
    :
    :
    default public boolean add(E element) {
        throw new UnsupportedOperationException();
    }
    :
    :
}
</code>

W ten sposób metoda „opcjonalna”add() ma domyślne zachowanie wyrzucania wyjątku UnsupportedOperationException, jeśli klasa implementująca nie zapewnia własnego nowego zachowania, co jest dokładnie tym, co chciałoby się zdarzyć i które jest zgodne z umową dla listy. Jeśli autor pisze klasę, która nie pozwala na dodawanie nowych elementów do implementacji listy, implementacjaadd() jest opcjonalne, ponieważ domyślne zachowanie jest dokładnie tym, czego potrzeba.

W tym przypadku „trzecia instrukcja” powyżej pozostaje prawdziwa, ponieważ metoda została zaimplementowana w samym interfejsie.

3. Metoda zwracającaOptional wynik

Ostatnim nowym rodzajem opcjonalnej metody jest po prostu metoda zwracającaOptional. TheOptional klasa zapewnia zdecydowanie bardziej zorientowany obiektowo sposób postępowanianull wyniki.

W płynnym stylu programowania, takim jak ten powszechnie spotykany przy kodowaniu za pomocą nowego interfejsu Java Streams API, wynik zerowy w dowolnym momencie powoduje awarię programu z wyjątkiem NullPointerException. TheOptional klasa zapewnia mechanizm zwracania pustych wyników do kodu klienta w sposób umożliwiający płynny styl bez powodowania awarii kodu klienta.

9

należy zaimplementować wszystkie metody (oprócz tych zdefault implementacje w Javie 8+), ale implementacja nie musi robić niczego użytecznego. W szczególności:

Może być pusta (pusta metoda).Może po prostu rzucićUnsupportedOperationException (lub podobne)

To drugie podejście jest często stosowane w klasach kolekcji - wszystkie metody są nadal zaimplementowane, ale niektóre mogą wywołać wyjątek, jeśli zostaną wywołane w czasie wykonywania.

3

że to jest oficjalny sposób

<code>public class Foo {
    public interface Callback {
        public void requiredMethod1();
        public void requiredMethod2();
    }

    public interface CallbackExtended extends Callback {
        public void optionalMethod1();
        public void optionalMethod2();
    }

    private Callback mCallback;
}
</code>

Jeśli twoja klasa nie potrzebuje implementować opcjonalnych metod, po prostu „implementuje Callback”. Jeśli twoja klasa potrzebuje implementować opcjonalne metody, po prostu „implementuje CallbackExtended”.

Przepraszam za gówno angielski.

Powiązane pytania