Dlaczego nie można rozszerzać adnotacji w Javie?

227

Nie rozumiem, dlaczego w adnotacjach Java nie ma dziedziczenia, podobnie jak w klasach Java. Myślę, że byłoby to bardzo przydatne.

Na przykład: Chcę wiedzieć, czy dana adnotacja jest walidatorem. Dzięki dziedziczeniu mogłem odruchowo poruszać się po superklasach, aby wiedzieć, czy ta adnotacja rozszerza ValidatorAnnotation. W przeciwnym razie, jak mogę to osiągnąć?

Czy ktoś może podać mi powód tej decyzji projektowej?

sinuhepop
źródło
2
Zauważ, BTW, że wszystkie adnotacje się rozciągają java.lang.annotation.Annotation, tzn. Każda adnotacja instanceof, chociaż fakt ten nie jest wyraźnie zadeklarowany.
Tomáš Záluský

Odpowiedzi:

166

O tym, dlaczego nie został zaprojektowany w ten sposób, można znaleźć odpowiedź w często zadawanych pytaniach dotyczących projektu JSR 175 , gdzie jest napisane:

Dlaczego nie obsługujecie podtypów adnotacji (gdzie jeden typ adnotacji rozszerza inny)?

Komplikuje to system typów adnotacji i znacznie utrudnia pisanie „określonych narzędzi”.

„Określone narzędzia” - programy, które wyszukują znane typy adnotacji w dowolnych programach zewnętrznych. Na przykład generatory odcinków należą do tej kategorii. Programy te odczytują klasy z adnotacjami bez ładowania ich na maszynę wirtualną, ale ładują interfejsy adnotacji.

Tak, tak myślę, powodem jest po prostu KISS. W każdym razie wydaje się, że ten problem (wraz z wieloma innymi) jest rozpatrywany jako część JSR 308 , a można nawet znaleźć alternatywny kompilator z tą funkcjonalnością, którą opracował już Mathias Ricken .

pedromarce
źródło
67
Cóż, może jestem głupi, ale myślę, że szkoda, że ​​nie mogę dodawać adnotacji tylko dla „upraszczania”. Przynajmniej projektanci Java nie myśleli tak samo o dziedziczeniu klas: P
sinuhepop
2
Java 8 M7 nie wydaje się obsługiwać adnotacji do podklas. Jaka szkoda.
Ceki,
2
@assylias JEP 104 nie polega na umożliwieniu podklasowania adnotacji. JEP 104 został zaimplementowany w Javie 8, ale nadal nie jest możliwe podklasowanie adnotacji (aby jedna adnotacja przedłużyła inną adnotację).
Jesper,
71

Rozszerzalne adnotacje skutecznie zwiększyłyby ciężar określania i utrzymywania innego systemu typów. Byłby to dość unikalny system typów, więc nie można po prostu zastosować paradygmatu typu OO.

Zastanów się nad wszystkimi problemami, gdy wprowadzasz polimorfizm i dziedziczenie adnotacji (np. Co się stanie, gdy subnotacja zmieni specyfikacje meta-adnotacji, takie jak retencja?)

A wszystko to dodatkowo komplikuje przypadek użycia?

Chcesz wiedzieć, czy dana adnotacja należy do kategorii?

Spróbuj tego:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    String category();
}

@Category(category="validator")
public @interface MyFooBarValidator {

}

Jak widać, można łatwo grupować i kategoryzować adnotacje bez zbędnego bólu, korzystając z udostępnionych udogodnień.

Tak więc KISS jest powodem, dla którego nie wprowadzono systemu typu meta-typ do języka Java.

[edycja ps]

Użyłem ciągu tylko do demonstracji i ze względu na otwartą meta adnotację. W przypadku własnego projektu możesz oczywiście użyć wyliczenia typów kategorii i podać wiele kategorii („wielokrotne dziedziczenie”) dla danej adnotacji. Pamiętaj, że wartości są całkowicie fałszywe i służą wyłącznie do celów demonstracyjnych:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    AnnotationCategory[] category();
}
public enum AnnotationCategory {
    GENERAL,
    SEMANTICS,
    VALIDATION,
    ETC
}

@Category(category={AnnotationCategory.GENERAL, AnnotationCategory.SEMANTICS})
public @interface FooBarAnnotation {

}

alphazero
źródło
Nie działa Wydrukuje fałsz:@Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) @interface C {}; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @C public @interface F {} class a{ @F public void S() {} } @Test public void blahTest() throws NoSuchMethodException { Method m = a.class.getMethod("S"); System.out.println(m.isAnnotationPresent(C.class)); }
użytkownik1615664
Adnotujemy adnotację. # S ma adnotację F. Sam F. jest opatrzony adnotacją C. Spróbuj tego.
alphazero
Tak to jest poprawne. Ale czy istnieje sposób, aby zrobić to bardziej czysto niż czytać za pośrednictwem proxy Adnotacji?
user1615664
12

W pewnym sensie masz to już z Adnotacjami - meta Adnotacjami. Jeśli adnotacja ma adnotację z meta informacją, jest to na wiele sposobów równoważne rozszerzeniu dodatkowego interfejsu. Adnotacje są interfejsami, więc polimorfizm tak naprawdę nie wchodzi w grę, a ponieważ mają charakter statyczny, nie można wywoływać dynamicznej emisji.

W swoim przykładzie walidatora możesz po prostu dodać adnotację i zobaczyć, czy ma ona meta-adnotację walidatora.

Jedynym przypadkiem, w którym mogłem zobaczyć, że dziedziczenie pomogłoby, jest to, że chcesz mieć adnotację według supertypu, ale to dodałoby całą złożoność, ponieważ dana metoda lub typ może mieć na sobie dwie takie adnotacje, co oznacza, że ​​tablica musiałaby zostać zwrócona zamiast tylko jednego obiektu.

Myślę więc, że ostateczną odpowiedzią jest to, że przypadki użycia są ezoteryczne i komplikują bardziej standardowe przypadki użycia, co sprawia, że ​​nie warto.

Yishai
źródło
5

Projektanci obsługi adnotacji Java wprowadzili szereg „uproszczeń” ze szkodą dla społeczności Java.

  1. Brak podtypów adnotacji powoduje, że wiele złożonych adnotacji jest niepotrzebnie brzydkich. W adnotacjach nie można po prostu mieć atrybutu, który może zawierać jedną z trzech rzeczy. Trzeba mieć trzy oddzielne atrybuty, które dezorientują programistów i wymagają sprawdzania poprawności w czasie wykonywania, aby upewnić się, że używany jest tylko jeden z trzech.

  2. Tylko jedna adnotacja danego typu na witrynę. Doprowadziło to do zupełnie niepotrzebnego wzorca adnotacji kolekcji. @Validation i @Validations, @Image i @Images itp.

Drugi jest naprawiany w Javie 8, ale jest już za późno. Wiele frameworków zostało napisanych w oparciu o to, co było możliwe w Javie 5, a teraz te brodawki API pozostaną na długo.

Konstantin Komissarchik
źródło
1
Ponadto uniemożliwia zdefiniowanie właściwości, które mają rekurencyjnie zagnieżdżone atrybuty - co jest obsługiwane przez schemat XML. To sprawia, że ​​adnotacje są znacznie mniej wydajne niż XML
user2833557
3

Być może spóźniłem się o trzy lata, odpowiadając na to pytanie, ale uznałem to za interesujące, ponieważ znalazłem się w tym samym miejscu. Oto moje zdanie na ten temat. Adnotacje można wyświetlać jako wyliczenia. Zapewniają one jednokierunkową informację - wykorzystaj ją lub zgub.

Miałem sytuację, w której chciałem symulować GET, POST, PUT i DELETE w aplikacji internetowej. Tak bardzo chciałem mieć „super” adnotację o nazwie „HTTP_METHOD”. Później dotarło do mnie, że to nie ma znaczenia. Cóż, musiałem zadowolić się użyciem ukrytego pola w formularzu HTML do identyfikacji DELETE i PUT (ponieważ POST i GET i tak były dostępne).

Po stronie serwera szukałem parametru ukrytego żądania o nazwie „_method”. Jeśli wartością było PUT lub DELETE, zastąpiło to powiązaną metodę żądania HTTP. Powiedziawszy to, nie miało znaczenia, czy muszę przedłużyć adnotację, aby wykonać pracę. Wszystkie adnotacje wyglądały tak samo, ale były traktowane inaczej po stronie serwera.

Więc w twoim przypadku upuść swędzenie, aby rozszerzyć adnotacje. Traktuj je jak „markery”. „Reprezentują” niektóre informacje i niekoniecznie „manipulują” niektórymi informacjami.

mainas
źródło
2

Jedną rzeczą, o której mógłbym pomyśleć, jest możliwość posiadania wielu adnotacji. Możesz więc dodać walidator i bardziej szczegółową adnotację w tym samym miejscu. Ale mogę się mylić :)

Janusz
źródło
2

Nigdy o tym nie myślałem, ale ... wydaje się, że masz rację, nie ma problemu z funkcją dziedziczenia adnotacji (przynajmniej nie widzę problemu z tym).

O twoim przykładzie z adnotacją „walidator” - możesz wtedy wykorzystać podejście „meta-adnotacja” . Oznacza to, że zastosujesz szczególną meta-adnotację do całego interfejsu adnotacji.

denis.zhdanov
źródło
Być może spóźniłem się o trzy lata, odpowiadając na to pytanie, ale uznałem to za interesujące, ponieważ znalazłem się w tym samym miejscu.
mainas
1

ten sam problem, który mam. Nie możesz. „Zdyscyplinowałem się”, aby pisać właściwości w adnotacjach, aby przestrzegać niektórych standardów, więc na zewnątrz, kiedy otrzymasz adnotację, możesz „wąchać”, jaki to rodzaj adnotacji według właściwości, które ma.

ante.sabo
źródło