Por que não é possível estender anotações em Java?


227

Não entendo por que não há herança nas anotações Java, assim como as classes Java. Eu acho que seria muito útil.

Por exemplo: quero saber se uma determinada anotação é um validador. Com a herança, eu poderia navegar reflexivamente pelas superclasses para saber se essa anotação estende a ValidatorAnnotation. Caso contrário, como posso conseguir isso?

Então, alguém pode me dar uma razão para essa decisão de design?


2
Observe, BTW, que todas as anotações se estendem java.lang.annotation.Annotation, ou seja, qualquer anotação instanceof, embora esse fato não seja explicitamente declarado.
Tomáš Záluský

Respostas:


166

Sobre o motivo pelo qual não foi projetado dessa maneira, você pode encontrar a resposta nas Perguntas frequentes sobre o design do JSR 175 , onde diz:

Por que você não oferece suporte à subtipagem de anotação (onde um tipo de anotação se estende a outro)?

Isso complica o sistema do tipo de anotação e torna muito mais difícil escrever "Ferramentas específicas".

...

“Ferramentas Específicas” - Programas que consultam tipos de anotação conhecidos de programas externos arbitrários. Os geradores de stub, por exemplo, se enquadram nessa categoria. Esses programas lerão classes anotadas sem carregá-las na máquina virtual, mas carregarão interfaces de anotação.

Então, sim, eu acho, o motivo é apenas o KISS. De qualquer forma, parece que esse problema (junto com muitos outros) está sendo analisado como parte do JSR 308 , e você pode até encontrar um compilador alternativo com essa funcionalidade já desenvolvida por Mathias Ricken .


67
Bem, talvez eu seja estúpido, mas acho que é uma pena não poder estender anotações apenas para "manter as coisas simples". Pelo menos, os designers Java não pensava o mesmo sobre herança de classe: P
sinuhepop

2
O Java 8 M7, não parece suportar anotações de subclassificação. Que pena.
Ceki

2
@assylias O JEP 104 não tem como objetivo possibilitar a subclasse de anotações. O JEP 104 foi implementado no Java 8, mas ainda não é possível sub-classificar anotações (faça uma anotação estender outra anotação).
Jesper

71

Anotações extensíveis aumentariam efetivamente o ônus de especificar e manter outro sistema de tipos. E esse seria um sistema de tipos bastante exclusivo, portanto você não poderia simplesmente aplicar um paradigma de tipo OO.

Pense em todos os problemas ao introduzir polimorfismo e herança em uma anotação (por exemplo, o que acontece quando a sub-anotação altera as especificações da meta-anotação, como retenção?)

E tudo isso adicionou complexidade para qual caso de uso?

Deseja saber se uma determinada anotação pertence a uma categoria?

Tente o seguinte:

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

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

}

Como você pode ver, você pode agrupar e categorizar anotações com facilidade, sem problemas desnecessários, usando os recursos fornecidos.

Portanto, o KISS é a razão para não introduzir um sistema do tipo meta-tipo na linguagem Java.

[ps editar]

Eu usei o String simplesmente para demonstração e em vista de uma meta-anotação aberta. Para o seu próprio projeto, obviamente, você pode usar uma enumeração de tipos de categoria e especificar várias categorias ("herança múltipla") para uma determinada anotação. Observe que os valores são totalmente falsos e apenas para fins de demonstração:

@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 {

}


Não funciona imprimirá falso:@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)); }
user1615664

Estamos anotando uma anotação. a # S possui a anotação F. F é anotada por C. Tente isso.
alphazero

Sim, está correto. Mas existe uma maneira de fazer isso de maneira mais limpa do que lendo o proxy de anotação?
user1615664

12

Em certo sentido, você já o possui com Anotações - meta Anotações. Se você anotar uma anotação com meta informações, isso é de várias maneiras equivalente à extensão de uma interface adicional. As anotações são interfaces, portanto o polimorfismo realmente não entra em jogo e, como são de natureza estática, não pode haver despacho dinâmico em tempo de execução.

No seu exemplo de validador, você pode obter apenas na anotação o tipo anotado e ver se ele possui uma meta-anotação de validador.

O único caso de uso que eu pude ver que a herança ajudaria é se você deseja obter a anotação por supertipo, mas isso adicionaria um monte de complexidade, porque um determinado método ou tipo pode ter duas anotações, significando que uma matriz teria que ser retornada em vez de apenas um único objeto.

Portanto, acho que a resposta final é que os casos de uso são esotéricos e complicam mais casos de uso padrão, não valendo a pena.


5

Os designers do suporte à anotação Java fizeram várias "simplificações" em detrimento da comunidade Java.

  1. Nenhum subtipo de anotação torna muitas anotações complexas desnecessariamente feias. Não se pode simplesmente ter um atributo em uma anotação que possa conter uma das três coisas. É necessário ter três atributos separados, o que confunde os desenvolvedores e requer validação de tempo de execução para garantir que apenas um dos três seja usado.

  2. Apenas uma anotação de um determinado tipo por site. Isso levou ao padrão de anotação de coleção completamente desnecessário. @Validation e @Validations, @Image e @Images, etc.

O segundo está sendo corrigido no Java 8, mas é tarde demais. Muitas estruturas foram escritas com base no que era possível no Java 5 e agora essas verrugas de API estão aqui para ficar por um bom tempo.


1
Além disso, também torna impossível definir propriedades com atributos aninhados recursivamente - que são suportados pelo esquema XML. Isso faz anotações estritamente menos poderoso do que XML
user2833557

3

Posso demorar três anos para responder a essa pergunta, mas achei interessante porque me encontrei no mesmo lugar. Aqui está a minha opinião. Você pode visualizar anotações como Enums. Eles fornecem um tipo de informação unidirecional - use-a ou perca-a.

Eu tive uma situação em que queria simular GET, POST, PUT e DELETE em um aplicativo da web. Eu queria muito ter uma anotação "super" chamada "HTTP_METHOD". Mais tarde, percebi que isso não importava. Bem, eu tive que resolver usando um campo oculto no formulário HTML para identificar DELETE e PUT (porque POST e GET estavam disponíveis de qualquer maneira).

No lado do servidor, procurei um parâmetro de solicitação oculta com o nome "_method". Se o valor foi PUT ou DELETE, substituiu o método de solicitação HTTP associado. Dito isto, não importava se eu precisava ou não estender uma anotação para concluir o trabalho. Todas as anotações pareciam iguais, mas foram tratadas de maneira diferente no lado do servidor.

Portanto, no seu caso, solte a coceira para estender as anotações. Trate-os como 'marcadores'. Eles "representam" algumas informações e não necessariamente "manipulam" algumas informações.


2

Uma coisa em que pude pensar é na possibilidade de ter várias anotações. Assim, você pode adicionar validador e uma anotação mais específica no mesmo local. Mas eu poderia estar enganado :)


2

Nunca pensei nisso, mas ... parece que você está certo, não há nenhum problema com o recurso de herança de anotações (pelo menos não vejo o problema).

Sobre o seu exemplo com a anotação 'validator' - você pode explorar a abordagem de 'meta-anotação' . Ou seja, você aplica uma meta-anotação específica a toda a interface da anotação.


Posso demorar três anos para responder a essa pergunta, mas achei interessante porque me encontrei no mesmo lugar.
mainas

1

o mesmo problema que tenho. Não, você não pode. Eu me 'disciplinei' para escrever propriedades em anotações para respeitar alguns padrões; portanto, quando você recebe anotações, pode 'farejar' que tipo de anotação é pelas propriedades que possui.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.