Kto rozszerza interfejsy? I dlaczego?

20

AFAIK, moje klasy klasy extendsnadrzędne i implementsinterfejsy. Ale natrafiam na sytuację, w której nie mogę użyć implements SomeInterface. Jest to deklaracja typów ogólnych. Na przykład:

public interface CallsForGrow {...}

public class GrowingArrayList <T implements CallsForGrow>  // BAD, won't work!
                              extends ArrayList<T> 

Tutaj używanie implementsjest składniowo zabronione. Najpierw pomyślałem, że używanie interfejsu wewnątrz <> jest w ogóle zabronione, ale nie. Jest to możliwe, muszę tylko użyć extendszamiast implements. W rezultacie „rozszerzam” interfejs. Ten kolejny przykład działa:

public interface CallsForGrow {...}

public class GrowingArrayList <T extends CallsForGrow>  // this works!
                              extends ArrayList<T> 

Wydaje mi się, że jest to niespójność składniowa. Ale może nie rozumiem niektórych zalet Javy 6? Czy są inne miejsca, w których powinienem rozszerzyć interfejsy? Czy interfejs, który mam rozszerzyć, powinien mieć jakieś specjalne funkcje?

Gangnus
źródło

Odpowiedzi:

25

W przypadku zmiennych typu ogólnego kompilator tak naprawdę nie dba o Tto, czy jest klasą, interfejsem, wyliczeniem czy adnotacją. Chodzi tylko o to, że jest to typ z danym zestawem podtypów i supertypów.

I nie ma powodu, aby komplikować składnię tylko dlatego, że w jednym innym miejscu (gdzie faktycznie implementujesz interfejs) rozróżnienie między klasami i interfejsami jest istotne (na przykład jeśli jesteś implementinterfejsem, musisz zaimplementować wszystkie zdefiniowane przez niego metody, ale ty nie musisz tego robić, jeśli jesteś extendklasą (nieabstrakcyjną).

Zakładając przez chwilę, że będziesz musiał implementstutaj napisać , potrzebujesz oddzielnej składni dla enumwartości (zamiast po prostu pisania <E extends Enum<E>>) i adnotacji (które możesz łatwo zadeklarować <A extends Annotation>).

Jedynym miejscem, w którym musisz pisać, implementsjest punkt, w którym faktycznie implementujesz interfejs. W tym momencie (i tylko w tym punkcie) różnica jest ważna, ponieważ musisz zaimplementować metody zdefiniowane w interfejsie. Dla wszystkich innych nie ma znaczenia, czy dana Aklasa jest klasą podstawową, czy zaimplementowanym interfejsem B: to supertyp i to wszystko, co się liczy.

Pamiętaj również, że jest jeszcze jedno miejsce, w którym korzystasz extendsz interfejsów:

interface A {
}

interface B extends A {
}

W tym momencie implementsbyłoby źle, ponieważ B nie wdraża A .

Joachim Sauer
źródło
Jeśli będziemy używać logiki, powinniśmy użyć „rozciąga SomeInterface” zawsze , nie tylko w generycznych.
Gangnus,
2
@Gangnus: nie, ponieważ dla implementatora istnieje konkretna różnica między extendsi implements, tylko patrząc na problem z perspektywy czystego systemu typów, nie ma różnicy.
Joachim Sauer
1
Przepraszam, nie rozumiem twojej myśli. - 1. Dlaczego w jednym przypadku powinniśmy używać „perspektywy czystego systemu typów”, a nie drugiej? --2. Dlaczego wymagania syntaktyczne zmieniają się w tych różnych miejscach? Czy możesz to wyjaśnić, proszę. W odpowiedzi, jeśli to możliwe.
Gangnus,
1
@Gangnus: kto mówi, że Tto klasa? Tmoże odwoływać się do typu interfejsu lub enumtypu.
Joachim Sauer
1
pochodząca z C # cała ta dyskusja jest absurdalna. oznaczamy nadtypami typu za pomocą :. a pod osłonami interfejs jest i zawsze był klasą abstrakcyjną z tym, że zawiera tylko niezaimplementowane elementy virtual ( abstract), aby umożliwić wielokrotne dziedziczenie. nie ma dosłownie żadnej różnicy między implementsi extends. oba można zastąpić tym samym słowem ( extends) lub dowolnym dowolnym symbolem ( :) i nic nie zostanie utracone.
sara,
5

Kiedy Java 5, a w szczególności generyczne, została początkowo udostępniona programistom, którzy zarejestrowali zainteresowanie, składnia była zupełnie inna. Zamiast Set<? extends Foo>i Set<? super Bar>miał Set<+Foo>i Set<-Foo>. Jednak informacja zwrotna była taka, że ​​nie było jasne, czy +oznacza bardziej szczegółowe czy szersze (więcej klas) . Firma Sun odpowiedziała na tę opinię, zmieniając składnię, ale pod warunkiem, że nie wprowadzi nowych słów kluczowych, co stanowiłoby problem z kompatybilnością wsteczną.

W rezultacie żaden z nich nie jest całkiem naturalny. Jak widać, extendsjest przeciążony, aby mówić o klasach „rozszerzających” interfejsy, co nie jest językiem używanym w innych kontekstach; i superjest przeciążony, co oznacza, że ​​„jest nadklasą”, która jest odwrotnym kierunkiem relacji wyrażonej wcześniej przez słowo kluczowe, tj. odnosi się do nadklasy.

Jednak zmiana ta nie ma wpływu na podstawowe elementy interfejsu i nie wprowadza specjalnych interfejsów, które zostały rozszerzone w nowy sposób.

Peter Taylor
źródło
+1. Widzę i zgadzam się. Ale Joachim Sauer uznał moje błędne myślenie, że T jest koniecznie klasą. Odpowiedź jest jego. Twoje zrozumienie jest o jedno (lub 2) piętro wyżej :-). Mój problem był bardziej prymitywny. W każdym razie dziękuję za mój rozwój.
Gangnus,
2

Istnieją zarówno wady zezwalające na taką składnię, jak i zabraniające, a te zezwalające są znacznie większe.

Pomyśl o tym.

Rozdzielenie interfejsu i implementacji jest jednym z podstawowych idiomów programistycznych. Z tego powodu składnia pozwalająca na przeliterowanie „interfejs implementuje coś” byłaby tak samo zła, jak użycie znaku plus do oznaczenia mnożenia operandów.

komar
źródło
Dobrze. Tak więc naprawdę czegoś nie rozumiem. Uważałem to za bardzo prawdopodobne. Ale nie wyjaśniłeś problemu, przepraszam. T jest reprezentacją klasy. Dlaczego w jednym miejscu używam rozszerzeń T, a w innych narzędziach T?
Gangnus
@ Gangnus w podanym przez Ciebie przykładzie T odnosi się do parametru typu, który niekoniecznie jest klasą - zobacz, co powiedział ci już Joachim . Zezwolenie na to dla narzędzi doprowadziłoby do tego samego bałaganu, o którym wspomniałem
gnat
Tak, widziałem już jego komentarz. Zostanie oznaczony jako odpowiedź po umieszczeniu w odpowiedzi. Aż oboje macie moje podziękowania i +1.
Gangnus
-1

Należy zrozumieć programowanie oparte na interfejsie jako kolejny krok podczas rozumienia interfejsu. Informuje, jakie jest rzeczywiste użycie interfejsu. Jaką rolę odgrywa w programie Java (lub innym języku).

Deepak
źródło
2
Jak twoja odpowiedź rozwiązuje obawy związane z PO? Proszę edytować swoją odpowiedź będzie bardziej jasne.