retention - what is a java annotation




Que anotação Java @NotNull devo usar? (14)

Android

Esta resposta é específica do Android. O Android tem um pacote de support-annotations chamado support-annotations . Isso fornece dozens de anotações http://tools.android.com/tech-docs/support-annotations do http://tools.android.com/tech-docs/support-annotations e também fornece anotações comuns como NonNull , Nullable etc.

Para adicionar o pacote de anotações de suporte , adicione a seguinte dependência em seu build.gradle:

compile 'com.android.support:support-annotations:23.1.1'

e depois use:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}

Eu estou olhando para tornar meu código mais legível, bem como usar ferramentas como inspeção de código IDE e / ou análise de código estático (FindBugs e Sonar) para evitar NullPointerExceptions. Muitas das ferramentas parecem incompatíveis com a anotação @NotNull / @NonNull / @Nonnull e listar todas elas no meu código seria terrível de ler. Alguma sugestão de qual é o "melhor"? Aqui está a lista de anotações equivalentes que encontrei:

  • javax.validation.constraints.NotNull
    Criado para validação de tempo de execução, não análise estática.
    documentation

  • edu.umd.cs.findbugs.annotations.NonNull
    Usado pela análise estática Findbugs e, portanto, Sonar (agora Sonarqube )
    documentation

  • javax.annotation.Nonnull
    Isso pode funcionar com o Findbugs também, mas o JSR-305 está inativo. (Veja também: Qual é o status da JSR 305? ) source

  • org.jetbrains.annotations.NotNull
    Usado pelo IntelliJ IDEA IDE para análise estática.
    documentation

  • lombok.NonNull
    Usado para controlar a geração de código no Projeto Lombok .
    Anotação de espaço reservado, pois não há padrão.
    source , documentation

  • android.support.annotation.NonNull
    Anotação de marcador disponível no Android, fornecida pelo pacote de anotações de suporte
    documentation

  • org.eclipse.jdt.annotation.NonNull
    Usado pelo Eclipse para análise de código estático
    documentation


Apenas apontando que a Java Validation API ( javax.validation.constraints.* ) Não vem com uma anotação @Nullable , que é muito valiosa em um contexto de análise estática. Faz sentido para a validação do bean de tempo de execução, pois este é o padrão para qualquer campo não primitivo em Java (ou seja, nada para validar / impor). Para os fins declarados, isso deve pesar para as alternativas.


De acordo com a lista de recursos do Java 7, as anotações do tipo JSR-308 são adiadas para o Java 8. As anotações do JSR-305 nem sequer são mencionadas.

Há um pouco de informação sobre o estado da JSR-305 em um appendix do último rascunho da JSR-308. Isso inclui a observação de que as anotações da JSR-305 parecem estar abandonadas. A página JSR-305 também mostra como "inativa".

Nesse meio tempo, a resposta pragmática é usar os tipos de anotação suportados pelas ferramentas mais usadas ... e estar preparado para alterá-los se a situação mudar.

Na verdade, o JSR-308 não define nenhum tipo / classe de anotação, e parece que eles estão fora do escopo. (E eles estão certos, dada a existência da JSR-305).

No entanto, se o JSR-308 realmente se parece com o Java 8, não me surpreenderia se o interesse pelo JSR-305 revivesse. AFAIK, a equipe da JSR-305 não abandonou formalmente o trabalho. Eles ficaram quietos por mais de dois anos.

É interessante que Bill Pugh (o líder técnico da JSR-305) é um dos caras por trás do FindBugs.


Distinguir entre análise estática e análise de tempo de execução. Use a análise estática para material interno e a análise de tempo de execução para os limites públicos de seu código.

Para coisas que não devem ser nulas:

  • Verificação de tempo de execução: Use "if (x == null) ..." (dependência zero) ou @ javax.validation.NotNull (com validação de bean) ou @ lombok.NonNull (simples e simples) ou goavas Preconditions.checkNotNull (.. .)

    • Use Opcional para tipos de retorno de método (somente). Java8 ou Goiaba.
  • Verificação estática: use uma anotação @NonNull

  • Onde couber, use as anotações @ ... NonnullByDefault em nível de classe ou pacote. Crie estas anotações você mesmo (exemplos são fáceis de encontrar).
    • Senão, use @ ... CheckForNull no método retorna para evitar NPEs

Isso deve fornecer o melhor resultado: avisos no IDE, erros por Findbugs e checkerframework, exceções de tempo de execução significativas.

Não espere que as verificações estáticas sejam maduras, sua nomenclatura não é padronizada e bibliotecas e IDEs diferentes as tratam de maneira diferente, ignore-as. As classes JSR305 javax.annotations. * Parecem padrão, mas não são, e causam pacotes divididos com Java9 +.

Algumas explicações das notas:

  • Anotações findbugs / spotbugs / jsr305 com o pacote javax.validation. * Confronto com outros módulos em Java9 +, também possivelmente violam a licença da Oracle
  • Anotações Spotbugs ainda depende de anotações jsr305 / findbugs no compiletime (no momento da escrita https://github.com/spotbugs/spotbugs/issues/421 )
  • O nome @NotNull do jetbrains entra em conflito com @ javax.validation.NotNull.
  • As anotações jetbrains, eclipse ou checkersframework para verificação estática têm a vantagem sobre javax.annotations de não entrarem em conflito com outros módulos em Java9 e superior
  • @ javax.annotations.Nullable não significa Findbugs / Spotbugs o que você (ou seu IDE) acha que significa. Findbugs irá ignorá-lo (nos membros). Triste, mas é verdade ( https://sourceforge.net/p/findbugs/bugs/1181 )
  • Para verificação estática fora de um IDE, existem 2 ferramentas gratuitas: Spotbugs (anteriormente Findbugs) e checkersframework.
  • A biblioteca Eclipse possui @NonNullByDefault, jsr305 tem apenas @ParametersAreNonnullByDefault. Esses são meros invólucros de conveniência que aplicam anotações base a tudo em um pacote (ou classe), você pode facilmente criar o seu próprio. Isso pode ser usado no pacote. Isso pode entrar em conflito com o código gerado (por exemplo, lombok).
  • As anotações do Eclipse jdt não são aplicáveis ​​aos retornos do método estático e alguns outros casos
  • O uso do lombok como uma dependência exportada deve ser evitado para bibliotecas que você compartilha com outras pessoas, quanto menos transitivas, melhor
  • Usar a estrutura de validação do Bean é poderoso, mas requer alta sobrecarga, o que é um exagero apenas para evitar a verificação manual de nulos.
  • Usando Opcional para campos e parâmetros de método é controverso (você pode encontrar artigos sobre isso facilmente)
  • As anotações nulas do Android fazem parte da biblioteca de suporte do Android, vêm com muitas outras classes e não funcionam bem com outras anotações / ferramentas

Antes do Java9, esta é minha recomendação:

// file: package-info.java
@javax.annotation.ParametersAreNonnullByDefault
package example;


// file: PublicApi
package example;

public class PublicApi {

    /**
     * @param firstname MUST NOT be null
     * @param lastname MUST NOT be null
     */
    public Person createPerson(
            // Spotbugs ignores the param annotations, but IDEs will show problems
            @Nullable String firstname, // Users  might send null
            @Nullable String lastname // Users might send null
            ) {
        if (firstname == null) throw new IllagalArgumentException(...);
        if (lastname == null) throw new IllagalArgumentException(...);
        return doCreatePerson(fistname, lastname, nickname);
    }

    @NonNull // Spotbugs checks that method cannot return null
    private Person doCreatePerson(
             String firstname, // Spotbugs checks null cannot be passed, because package has ParametersAreNonnullByDefault
             String lastname,
             @Nullable String nickname // tell Spotbugs null is ok
             ) {
         return new Person(firstname, lastname, nickname);
    }

    @CheckForNull // Do not use @Nullable here, Spotbugs will ignore it, though IDEs respect it
    private Person getNickname(
         String firstname,
         String lastname) {
         return NICKNAMES.get(firstname + ':' + lastname);
    }
}

Note que não há nenhuma maneira de fazer Spotbugs levantar um aviso quando um parâmetro do método anulável é desreferenciado (no momento da escrita, versão 3.1 do Spotbugs). Talvez o checkerframework possa fazer isso.


Eu gosto muito do Checker Framework , que é uma implementação de anotações de tipo ( JSR-308 ) que é usada para implementar verificadores de defeitos como um verificador de nulidade. Eu realmente não tentei nenhum outro para oferecer qualquer comparação, mas eu fiquei feliz com essa implementação.

Não sou afiliado ao grupo que oferece o software, mas sou fã.

Quatro coisas que eu gosto sobre este sistema:

  1. Ele tem um verificador de defeitos para nullness (@Nullable), mas também tem uns para immutability e interning (e outros). Eu uso o primeiro (nulidade) e estou tentando usar o segundo (imutabilidade / IGJ). Estou experimentando o terceiro, mas não tenho certeza sobre o uso a longo prazo ainda. Eu não estou convencido da utilidade geral dos outros verificadores ainda, mas é bom saber que o framework em si é um sistema para implementar uma variedade de anotações e verificadores adicionais.

  2. A configuração padrão para verificação de nulidade funciona bem: Não nula, exceto locais (NNEL). Basicamente, isso significa que, por padrão, o verificador trata everyhing (variáveis ​​de instância, parâmetros de método, tipos genéricos etc.), exceto variáveis ​​locais como se tivessem um tipo @NonNull por padrão. Pela documentação:

    O padrão do NNEL leva ao menor número de anotações explícitas no seu código.

    Você pode definir um padrão diferente para uma classe ou para um método se o NNEL não funcionar para você.

  3. Este framework permite que você use sem criar uma dependência no framework colocando suas anotações em um comentário: por exemplo, /*@Nullable*/ . Isso é bom porque você pode anotar e verificar uma biblioteca ou um código compartilhado, mas ainda pode usar essa biblioteca / código compartilhado em outro projeto que não usa o framework. Este é um bom recurso. Eu me acostumei a usá-lo, embora eu tenha a tendência de habilitar o Checker Framework em todos os meus projetos agora.

  4. A estrutura tem uma maneira de anotar as APIs que você usa que ainda não foram anotadas para nullness usando arquivos stub.


Eu uso o IntelliJ, porque estou mais preocupado com o IntelliJ sinalizando coisas que podem produzir um NPE. Eu concordo que é frustrante não ter uma anotação padrão no JDK. Fala-se em adicioná-lo, ele pode entrar no Java 7. Nesse caso, haverá mais um para escolher!


JSR305 e FindBugs são criados pela mesma pessoa. Ambos são mal conservados, mas são tão padrão quanto possível e são suportados por todos os principais IDEs. A boa notícia é que eles funcionam bem como estão.

Aqui está como aplicar @Nonnull a todas as classes, métodos e campos por padrão. Consulte https://.com/a/13319541/14731 e https://.com/a/9256595/14731

  1. Definir @NotNullByDefault
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;


    /**
     * This annotation can be applied to a package, class or method to indicate that the class fields,
     * method return types and parameters in that element are not null by default unless there is: <ul>
     * <li>An explicit nullness annotation <li>The method overrides a method in a superclass (in which
     * case the annotation of the corresponding parameter in the superclass applies) <li> there is a
     * default parameter annotation applied to a more tightly nested element. </ul>
     * <p/>
     * @see https://.com/a/9256595/14731
     */
    @Documented
    @Nonnull
    @TypeQualifierDefault(
    {
        ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR,
        ElementType.FIELD,
        ElementType.LOCAL_VARIABLE,
        ElementType.METHOD,
        ElementType.PACKAGE,
        ElementType.PARAMETER,
        ElementType.TYPE
    })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NotNullByDefault
    {
    }

2. Adicione a anotação para cada pacote: package-info.java

@NotNullByDefault
package com.example.foo;

ATUALIZAÇÃO : A partir de 12 de dezembro de 2012, o JSR 305 é listado como "Inativo". Segundo a documentação:

Uma JSR que foi votada como "inativa" pelo Comitê Executivo, ou uma que atingiu o fim de sua vida útil natural.

Parece que o JSR-308 está entrando no JDK 8 e, embora o JSR não defina @NotNull, o Checkers Framework o acompanha. No momento da redação deste artigo, o plugin Maven está inutilizável devido a este bug: https://github.com/typetools/checker-framework/issues/183



Se alguém está apenas procurando pelas classes IntelliJ: você pode obtê-las do repositório maven com

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>15.0</version>
</dependency> 

Se você está desenvolvendo para o Android, você está um pouco amarrado ao Eclipse (editar: no momento da escrita, não mais), que tem suas próprias anotações. Está incluído no Eclipse 3.8+ (Juno), mas desabilitado por padrão.

Você pode ativá-lo em Preferências> Java> Compilador> Erros / Avisos> Análise nula (seção recolhível na parte inferior).

Marque a opção "Ativar análise nula baseada em anotação"

http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage tem recomendações sobre configurações. No entanto, se você tiver projetos externos em seu espaço de trabalho (como o SDK do facebook), eles podem não atender a essas recomendações, e você provavelmente não desejará corrigi-los com cada atualização do SDK ;-)

Eu uso:

  1. Acesso a ponteiro nulo: erro
  2. Violação da especificação nula: erro (ligado ao ponto # 1)
  3. Potencial acesso a ponteiro nulo: Aviso (caso contrário, o SDK do facebook teria avisos)
  4. Conflito entre anotações nulas e inferência nula: Aviso (ligado ao ponto # 3)


Outra opção são as anotações fornecidas com ANTLR 4. Após o Pedido de Recebimento # 434 , o artefato contendo as anotações @NotNulle @Nullableinclui um processador de anotação que produz erros e / ou avisos em tempo de compilação no caso de um desses atributos ser usado incorretamente (por exemplo, ambos são aplicados ao mesmo item ou se @Nullableaplicados a um item com um tipo primitivo). O processador de anotações fornece garantia adicional durante o processo de desenvolvimento de software de que as informações transmitidas pela aplicação dessas anotações são precisas, inclusive em casos de herança de métodos.


Se você estiver trabalhando em um projeto grande, você pode ser melhor de criar o seu próprio @Nullable e / ou @NotNullanotações.

Por exemplo:

@java.lang.annotation.Documented
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS)
@java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD,
                              java.lang.annotation.ElementType.METHOD,    
                              java.lang.annotation.ElementType.PARAMETER,
                              java.lang.annotation.ElementType.LOCAL_VARIABLE})
public @interface Nullable 
{
}

Se você usar a política de retenção correta , as anotações não estarão disponíveis no tempo de execução . Deste ponto de vista, é apenas uma coisa interna .

Mesmo que isso não seja uma ciência rigorosa, acho que faz mais sentido usar uma classe interna para isso.

  • É uma coisa interna. (sem impacto funcional ou técnico)
  • Com muitos muitos usos.
  • IDE como o IntelliJ suporta anotações @Nullable/ @NotNullanotações personalizadas .
  • A maioria das estruturas prefere usar sua própria versão interna também.

Perguntas Adicionais (ver comentários):

Como configurar isso no IntelliJ?

Clique no "policial" no canto inferior direito da barra de status do IntelliJ. E clique em "Configurar inspeções" no pop-up. Próximo ...


Uma das coisas boas do IntelliJ é que você não precisa usar as anotações. Você pode escrever o seu próprio, ou você pode usar os de qualquer outra ferramenta que você gosta. Você não está limitado a um único tipo. Se você estiver usando duas bibliotecas que usam anotações @NotNull diferentes, poderá informar ao IntelliJ para usar as duas. Para fazer isso, vá para "Configurar inspeções", clique na inspeção "Condições constantes e exceções" e pressione o botão "Configurar inspeções". Eu uso o Nullness Checker sempre que posso, então eu configurei o IntelliJ para usar essas anotações, mas você pode fazê-lo funcionar com qualquer outra ferramenta que você queira. (Eu não tenho opinião sobre as outras ferramentas porque eu tenho usado as inspeções da IntelliJ por anos, e eu as amo.)





ide