Por que uma anotação ausente não causa uma ClassNotFoundException no tempo de execução?


91

Considere o seguinte código:

A.java:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface A{}

C.java:

import java.util.*;

@A public class C {
        public static void main(String[] args){
                System.out.println(Arrays.toString(C.class.getAnnotations()));
        }
}

Compilar e executar funciona conforme o esperado:

$ javac *.java
$ java -cp . C
[@A()]

Mas então considere isto:

$ rm A.class
$ java -cp . C
[]

Eu esperava que ele jogasse um ClassNotFoundException, já que @Aestá faltando. Mas em vez disso, ele descarta silenciosamente a anotação.

Esse comportamento está documentado no JLS em algum lugar ou é uma peculiaridade da JVM da Sun? Qual é a razão disso?

Parece conveniente para coisas como javax.annotation.Nonnull(o que parece que deveria ser de @Retention(CLASS)qualquer maneira), mas para muitas outras anotações, parece que pode causar várias coisas ruins em tempo de execução.

Respostas:


90

Nos rascunhos públicos anteriores para JSR-175 (anotações), foi discutido se o compilador e o tempo de execução deveriam ignorar anotações desconhecidas, para fornecer um acoplamento mais flexível entre o uso e a declaração de anotações. Um exemplo específico foi o uso de anotações específicas do servidor de aplicativos em um EJB para controlar a configuração de implantação. Se o mesmo bean devesse ser implantado em um servidor de aplicativos diferente, seria conveniente se o tempo de execução simplesmente ignorasse as anotações desconhecidas em vez de gerar um NoClassDefFoundError.

Mesmo que o texto seja um pouco vago, suponho que o comportamento que você está vendo é especificado em JLS 13.5.7: "... a remoção de anotações não tem efeito na ligação correta das representações binárias de programas na linguagem de programação Java . " Eu interpreto isso como se as anotações fossem removidas (não disponíveis no tempo de execução), o programa ainda deveria ser vinculado e executado e que isso implica que as anotações desconhecidas são simplesmente ignoradas quando acessadas por meio de reflexão.

A primeira versão do JDK 5 da Sun não implementou isso corretamente, mas foi corrigido em 1.5.0_06. Você pode encontrar o bug relevante 6322301 no banco de dados de bug, mas ele não aponta para nenhuma especificação, exceto alegando que "de acordo com a especificação do JSR-175, anotações desconhecidas devem ser ignoradas por getAnnotations".


35

Citando o JLS:

9.6.1.2 As anotações de retenção podem estar presentes apenas no código-fonte, ou podem estar presentes na forma binária de uma classe ou interface. Uma anotação que está presente no binário pode ou não estar disponível em tempo de execução por meio das bibliotecas reflexivas da plataforma Java.

O tipo de anotação annotation.Retention é usado para escolher entre as possibilidades acima. Se uma anotação a corresponde a um tipo T, e T tem uma (meta-) anotação m que corresponde à anotação. Retenção, então:

  • Se m tiver um elemento cujo valor seja annotation.RetentionPolicy.SOURCE, um compilador Java deve garantir que a não esteja presente na representação binária da classe ou interface em que aparece.
  • Se m tiver um elemento cujo valor é annotation.RetentionPolicy.CLASS ou annotation.RetentionPolicy.RUNTIME, um compilador Java deve garantir que a seja representado na representação binária da classe ou interface em que a aparece, a menos que m anote uma declaração de variável local . Uma anotação em uma declaração de variável local nunca é retida na representação binária.

Se T não tiver uma (meta-) anotação m que corresponda a annotation.Retention, um compilador Java deve tratar T como se tivesse essa meta-anotação m com um elemento cujo valor é annotation.RetentionPolicy.CLASS.

Portanto, RetentionPolicy.RUNTIME garante que a anotação seja compilada no binário, mas uma anotação presente no binário não precisa estar disponível em tempo de execução


9

se você realmente tem um código que lê @A e faz algo com ele, o código depende da classe A e lançará ClassNotFoundException.

se não, ou seja, nenhum código se preocupa especificamente com @A, então é discutível que @A realmente não importa.

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.