Qual anotação @NotNull Java devo usar?

999

Eu estou procurando tornar meu código mais legível e usar ferramentas como inspeção de código IDE e / ou análise de código estática (FindBugs e Sonar) para evitar NullPointerExceptions. Muitas das ferramentas parecem incompatíveis com a anotação / @NotNull/ da outra 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:@NonNull@Nonnull

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

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

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

  • org.jetbrains.annotations.NotNull
    Usado pelo IntelliJ IDEA IDE para análise estática.
    documentação

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

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

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

jaxzin
fonte
203
O apache deve inventar uma anotação "comum" e uma ferramenta que possa convertê-la em qualquer outra anotação. a solução para o problema de muitos padrões é inventar um novo padrão.
irreputável
6
@ irreputable se o apache inventar um novo "comum", haveria 56 versões dele, sobrepondo-se a outros projetos. E, de qualquer maneira, não seria padrão (padrão! = Generalizado). Melhor usar qualquer coisa realmente padrão, javax?. *. BTW, não há "muitos padrões" nesses exemplos, eu só ver 1 ou 2.
ymajoros
6
O javax.annotation.Nonnull funciona com findbugs (apenas testado), o que é uma razão convincente para eu usá-lo.
Nicolas C
20
Se eu simplesmente escrever @NotNull, isso se refere a com.sun.istack.internal.NotNull. OMG ... #
6306 Thomas Weller
3
@MozartBrocchini - Os opcionais são úteis nos casos em que você possa ter usado anteriormente o NullObjects. Eles realmente não atendem ao mesmo objetivo que uma anotação \ @NotNull em tempo de execução e introduzem desembrulhar tedioso.
19716 Dave

Respostas:

205

Como o JSR 305 (cujo objetivo era padronizar @NonNulle @Nullable) permanece inativo por vários anos, receio que não haja uma boa resposta. Tudo o que podemos fazer é encontrar uma solução pragmática e a minha é a seguinte:

Sintaxe

Do ponto de vista puramente estilístico, eu gostaria de evitar qualquer referência ao IDE, framework ou qualquer kit de ferramentas, exceto o próprio Java.

Isso exclui:

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations
  • org.checkerframework.checker.nullness.qual
  • lombok.NonNull

O que nos deixa com javax.validation.constraintsou javax.annotation. O primeiro vem com o JEE. Se isso é melhor do que javax.annotation, o que pode vir eventualmente com o JSE ou nunca, é uma questão de debate. Pessoalmente, prefiro javax.annotationporque não gostaria da dependência do JEE.

Isso nos deixa com

javax.annotation

que também é o mais curto.

Há apenas uma sintaxe que seria ainda melhor: java.annotation.Nullable. Como outros pacotes passaram javaxpara javao passado, a anotação javax.an seria um passo na direção certa.

Implementação

Eu esperava que todos eles tivessem basicamente a mesma implementação trivial, mas uma análise detalhada mostrou que isso não é verdade.

Primeiro pelas semelhanças:

Todas as @NonNullanotações têm a linha

public @interface NonNull {}

exceto por

  • org.jetbrains.annotationsque chama @NotNulle tem uma implementação trivial
  • javax.annotation que tem uma implementação mais longa
  • javax.validation.constraintsque também chama @NotNulle tem uma implementação

Todas as @Nullableanotações têm a linha

public @interface Nullable {}

exceto (novamente) o org.jetbrains.annotationscom sua implementação trivial.

Para as diferenças:

Um impressionante é que

  • javax.annotation
  • javax.validation.constraints
  • org.checkerframework.checker.nullness.qual

todos têm anotações em tempo de execução ( @Retention(RUNTIME)), enquanto

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations

são apenas tempo de compilação ( @Retention(CLASS)).

Conforme descrito nesta resposta do SO, o impacto das anotações em tempo de execução é menor do que se imagina, mas elas têm o benefício de permitir que as ferramentas realizem verificações em tempo de execução, além das de tempo de compilação.

Outra diferença importante é onde no código as anotações podem ser usadas. Existem duas abordagens diferentes. Alguns pacotes usam contextos de estilo JLS 9.6.4.1. A tabela a seguir fornece uma visão geral:

                                PARÂMETRO DO MÉTODO DE CAMPO LOCAL_VARIABLE 
android.support.annotation XXX   
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation XXXX
lombok XXXX
javax.validation.constraints XXX   

org.eclipse.jdt.annotation, javax.annotationE org.checkerframework.checker.nullness.qualusar os contextos definidos no JLS 4,11, o que na minha opinião é o caminho certo para fazê-lo.

Isso nos deixa com

  • javax.annotation
  • org.checkerframework.checker.nullness.qual

nesta rodada.

Código

Para ajudá-lo a comparar mais detalhes, listo o código de todas as anotações abaixo. Para facilitar a comparação, removi comentários, importações e @Documentedanotações. (todos eles tinham, @Documentedexceto as classes do pacote Android). Reordenei as linhas e os @Targetcampos e normalizei as qualificações.

package android.support.annotation;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER})
public @interface NonNull {}

package edu.umd.cs.findbugs.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}

package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface NonNull {}

package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NotNull {String value() default "";}

package javax.annotation;
@TypeQualifier
@Retention(RUNTIME)
public @interface Nonnull {
    When when() default When.ALWAYS;
    static class Checker implements TypeQualifierValidator<Nonnull> {
        public When forConstantValue(Nonnull qualifierqualifierArgument,
                Object value) {
            if (value == null)
                return When.NEVER;
            return When.ALWAYS;
        }
    }
}

package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf(MonotonicNonNull.class)
@ImplicitFor(
    types = {
        TypeKind.PACKAGE,
        TypeKind.INT,
        TypeKind.BOOLEAN,
        TypeKind.CHAR,
        TypeKind.DOUBLE,
        TypeKind.FLOAT,
        TypeKind.LONG,
        TypeKind.SHORT,
        TypeKind.BYTE
    },
    literals = {LiteralKind.STRING}
)
@DefaultQualifierInHierarchy
@DefaultFor({TypeUseLocation.EXCEPTION_PARAMETER})
@DefaultInUncheckedCodeFor({TypeUseLocation.PARAMETER, TypeUseLocation.LOWER_BOUND})
public @interface NonNull {}

Para completar, aqui estão as @Nullableimplementações:

package android.support.annotation;
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD})
public @interface Nullable {}

package edu.umd.cs.findbugs.annotations;
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
@Retention(CLASS)
public @interface Nullable {}

package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface Nullable {}

package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface Nullable {String value() default "";}

package javax.annotation;
@TypeQualifierNickname
@Nonnull(when = When.UNKNOWN)
@Retention(RUNTIME)
public @interface Nullable {}

package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf({})
@ImplicitFor(
    literals = {LiteralKind.NULL},
    typeNames = {java.lang.Void.class}
)
@DefaultInUncheckedCodeFor({TypeUseLocation.RETURN, TypeUseLocation.UPPER_BOUND})
public @interface Nullable {}

Os dois pacotes a seguir não têm @Nullable, então eu os listo separadamente; Lombok é muito chato @NonNull. Em javax.validation.constraintso @NonNullé na verdade um @NotNull e tem uma implementação comprido.

package lombok;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}

package javax.validation.constraints;
@Retention(RUNTIME)
@Target({ FIELD, METHOD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Constraint(validatedBy = {})
public @interface NotNull {
    String message() default "{javax.validation.constraints.NotNull.message}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default {};
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        NotNull[] value();
    }
}

Apoio, suporte

Pela minha experiência, javax.annotationé pelo menos suportado pelo Eclipse e pelo Checker Framework pronto para uso.

Sumário

Minha anotação ideal seria a java.annotationsintaxe com a implementação do Checker Framework.

Se você não pretende usar o Checker Framework, o javax.annotation( JSR-305 ) ainda é sua melhor aposta no momento.

Se você deseja comprar o Checker Framework, basta usá-lo org.checkerframework.checker.nullness.qual.


Fontes

  • android.support.annotation de android-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotations de findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotation de org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotations de jetbrains-annotations-13.0.jar
  • javax.annotation de gwt-dev-2.5.1-sources.jar
  • org.checkerframework.checker.nullness.qual de checker-framework-2.1.9.zip
  • lombokde lombokconfirmarf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints de validation-api-1.0.0.GA-sources.jar
Ludwig Weinzierl
fonte
7
A desvantagem javax.annotationé que é a) baseado em um JSR morto, b) difícil encontrar um artefato que apenas forneça as anotações e seja mantido. O de findbugs não é: search.maven.org/…
robinst
18
Outro ponto contrário javax.annotationé que ele causa problemas com o Java 9 porque outros módulos também fornecem classes nesse pacote (jax-ws).
robinst 28/09
10
@kevinarpe: O projeto Findbugs está morto e o projeto sucessor Spotbugs está removendo essas anotações: github.com/spotbugs/spotbugs/pull/180
robinst
4
O JSR 305 , que teria sido padronizado javax.annotation.NonNull, nunca foi concluído porque seu lead de especificação ficou em falta. Não teve nada a ver com nenhuma decisão da Oracle.
22618 Mark Reinhold
5
Outro motivo para não usar o jsr305.jar é que ele aparentemente viola a licença binária do Oracle Java: github.com/google/guava/issues/2960
Flow
91

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 outros para oferecer nenhuma comparação, mas fiquei feliz com essa implementação.

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

Quatro coisas que eu gosto neste sistema:

  1. Possui um verificador de defeitos para nulidade (@Nullable), mas também possui um para imutabilidade e internação (e outros). Eu uso o primeiro (nulidade) e estou tentando entrar no segundo (imutabilidade / IGJ). Estou testando o terceiro, mas ainda não tenho certeza sobre o uso a longo prazo. Ainda não estou convencido da utilidade geral dos outros verificadores, mas é bom saber que a estrutura 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 nulo, exceto locais (NNEL). Basicamente, isso significa que, por padrão, o verificador trata tudo (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. De acordo com a documentação:

    O padrão 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 método, se o NNEL não funcionar para você.

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

  4. A estrutura possui uma maneira de anotar APIs usadas que ainda não foram anotadas para nulidade usando arquivos stub.

Bert F
fonte
3
Parece ótimo e eu gostaria de usá-lo, mas não posso. Por que GPL? Não poderia ser a LGPL?
Burkhard #
13
De acordo com a FAQ : "A licença MIT mais permissiva se aplica ao código que você pode querer incluir em seu próprio programa, como as anotações".
Seanf
1
Os links estão quebrados no momento. Mas +1 para obter conselhos sobre o uso do Checker Framework.
Paul Wagland
1
É uma pena que os verificadores de imutabilidade sejam descartados na versão mais recente.
Franklin Yu
1
O Checker Framework também é sugerido no Oracle Java Tutorials .
Quazi Irfan
55

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

Sam Barnum
fonte
68
Update: IntelliJ agora suporta todas as anotações acima para destacar código, para que você não está restrito às anotações do IntelliJ mais: blogs.jetbrains.com/idea/2011/03/...
Daniel Alexiuc
31
E o Eclipse Juno também!
jFrenetic
5
javax.annotation.Nonnullé mais amplamente aceito, não é?
317 Martin
1
@DanielAlexiuc Mas, infelizmente, ele não usá-los para suas verificações de tempo de execução, por isso ainda há um benefício de usar os JetBrains queridos ...
Trejkaz
4
@Trejkaz Desde 2016.3, ele cria verificações de tempo de execução para todos eles.
Karol S
32

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 são mencionadas.

Há algumas informações sobre o estado do JSR-305 em um apêndice do rascunho mais recente do JSR-308. Isso inclui a observação de que as anotações do JSR-305 parecem 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.


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

No entanto, se o JSR-308 realmente parecer entrar no Java 8, não me surpreenderia se o interesse no JSR-305 revivesse. AFAIK, a equipe JSR-305 não abandonou formalmente seu trabalho. Eles ficaram quietos por mais de 2 anos.

É interessante que Bill Pugh (o líder técnico do JSR-305) seja um dos responsáveis ​​por FindBugs.

Stephen C
fonte
4
@pst - o calendário atual é para Java 8 para ir para lançamento geral em setembro de 2013 - infoq.com/news/2012/04/jdk-8-milestone-release-dates
Stephen C
2
Isso caiu para março de 2014 agora - openjdk.java.net/projects/jdk8 . O JSR 308 está incluído no build M7 (consulte "104 - Anotações nos tipos Java").
Stephen C
28

Para projetos Android, você deve usar android.support.annotation.NonNulle android.support.annotation.Nullable. Essas e outras anotações úteis específicas do Android estão disponíveis na Biblioteca de suporte .

Em http://tools.android.com/tech-docs/support-annotations :

A própria biblioteca de suporte também foi anotada com essas anotações; portanto, como usuário da biblioteca de suporte, o Android Studio já verificará seu código e sinalizará problemas em potencial com base nessas anotações.

James Wald
fonte
3
Seria útil fornecer justificativa para essa recomendação.
damasco
2
tools.android.com/tech-docs/support-annotations "A própria biblioteca de suporte também foi anotada com essas anotações; portanto, como usuário da biblioteca de suporte, o Android Studio já verificará seu código e sinalizará problemas potenciais com base nessas anotações . "
James Wald
3
BTW Android Estúdio apoio JSR305 com javax.annotation.*anotações também
CAMOBAP
19

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

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>15.0</version>
</dependency> 
Bruno Eberhard
fonte
É isso que faz com que o Intellij emita avisos, sim.
Click UpVote
A versão atual (em 05/2017) é 15.0
BamaPookie 16/05
Você está certo. Eu atualizei a versão. Mesmo se eu acho que não mudou muito.
Bruno Eberhard
Lembre-se de que as anotações do JetBrains não são retidas para o tempo de execução; portanto, o suporte ao Guice @Nullable, por exemplo, não funciona com ele.
Peter major
18

JSR305 e FindBugs são de autoria da mesma pessoa. Ambos são mal mantidos, 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 o @Nonnull a todas as classes, métodos e campos por padrão. Consulte https://stackoverflow.com/a/13319541/14731 e https://stackoverflow.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://stackoverflow.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 a cada pacote: package-info.java

@NotNullByDefault
package com.example.foo;

ATUALIZAÇÃO : em 12 de dezembro de 2012, o JSR 305 está listado como "Inativo". De acordo com a documentação:

Um JSR que foi votado como "inativo" pelo Comitê Executivo ou 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 acompanhamento o Checkers Frameworkfaz. No momento da redação deste artigo, o plug-in Maven não pode ser usado devido a este bug: https://github.com/typetools/checker-framework/issues/183

Gili
fonte
2
O problema showstopper para o maven foi corrigido. Portanto, isso deve ser uma opção novamente.
Marc von Renteln
Eu uso o FindBugs via Maven, nada é feito pelo meu IDE, isso evita anotações específicas do IDE, o que você recomendaria?
Christophe Roussy
@ChristopheRoussy Sua pergunta é específica do IDE. Por favor, abra uma pergunta separada.
Gili
15

Distinguir entre análise estática e análise de tempo de execução. Use análise estática para coisas internas e análise de tempo de execução para os limites públicos do 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 (puro e simples) ou goiabas Preconditions.checkNotNull (.. .)

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

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

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

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

Algumas notas explicativas:

  • As anotações Findbugs / spotbugs / jsr305 com o pacote javax.validation. * Colidem com outros módulos no Java9 +, também possivelmente violam a licença do Oracle
  • As anotações de spotbugs ainda dependem das anotações jsr305 / findbugs no compiletime (no momento da redação https://github.com/spotbugs/spotbugs/issues/421 )
  • jetbrains O nome @NotNull entra em conflito com @ javax.validation.NotNull.
  • As anotações de jetbrains, eclipse ou checkersframework para verificação estática têm a vantagem sobre javax.notas de que elas não se chocam com outros módulos em Java9 e superior
  • @ javax.annotations.Nullable não significa para Findbugs / Spotbugs o que você (ou seu IDE) pensa que isso significa. Findbugs irá ignorá-lo (nos membros). Triste, mas verdadeiro ( 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 do Eclipse possui @NonNullByDefault, jsr305 possui apenas @ParametersAreNonnullByDefault. Esses são meros invólucros de conveniência que aplicam anotações básicas a tudo em um pacote (ou classe), você pode criar facilmente seus próprios. Isso pode ser usado no pacote. Isso pode entrar em conflito com o código gerado (por exemplo, lombok).
  • O uso do lombok como uma dependência exportada deve ser evitado para bibliotecas que você compartilha com outras pessoas, quanto menos dependências transitivas, melhor
  • O uso da estrutura de validação de Bean é poderoso, mas requer uma sobrecarga alta, portanto é um exagero apenas para evitar a verificação nula manual.
  • O uso de 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, elas 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 interface PublicApi {

    Person createPerson(
        // NonNull by default due to package-info.java above
        String firstname,
        String lastname);
}

// file: PublicApiImpl
public class PublicApiImpl implements PublicApi {
    public Person createPerson(
            // In Impl, handle cases where library users still pass null
            @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);
    }
}

Observe que não há como fazer com que os Spotbugs gerem um aviso quando um parâmetro de método anulável é desreferenciado (no momento da redação, versão 3.1 do Spotbugs). Talvez o checkerframework possa fazer isso.

Infelizmente, essas anotações não fazem distinção entre os casos de um método público de uma biblioteca com chamadas arbitrárias e métodos não públicos nos quais cada site de chamada pode ser conhecido. Portanto, o duplo significado de: "Indique que nulo é indesejável, mas prepare-se para que nulo seja passado" não é possível em uma única declaração; portanto, o exemplo acima tem anotações diferentes para a interface e a implementação.

Nos casos em que a abordagem de interface dividida não é prática, a seguinte abordagem é um compromisso:

        public Person createPerson(
                @NonNull String firstname,
                @NonNull String lastname
                ) {
            // even though parameters annotated as NonNull, library clients might call with null.
            if (firstname == null) throw new IllagalArgumentException(...);
            if (lastname == null) throw new IllagalArgumentException(...);
            return doCreatePerson(fistname, lastname, nickname);
        }

Isso ajuda os clientes a não passarem nulos (escrevendo o código correto), enquanto retornam erros úteis, se o fizerem.

tkruse
fonte
Encontrei esta resposta apenas agora, mas @tkruse, onde você encontrou: "As anotações do Eclipse jdt não são aplicáveis ​​aos retornos de métodos estáticos e a alguns outros casos"? (a primeira parte não é verdadeira, a segunda é bastante vaga :)).
Stephan Herrmann
@ StephanHerrmann: Não me lembro. Eu removi o ponto de bala.
tkruse
12

O Eclipse também possui suas próprias anotações.

org.eclipse.jdt.annotation.NonNull

Veja em http://wiki.eclipse.org/JDT_Core/Null_Analysis para obter detalhes.

Horcrux7
fonte
Parece que isso será integrado a partir do Eclipse 3.8 (Juno), que alinhará o Eclipse com o IntelliJ a esse respeito. Também deve permitir que você configure suas próprias anotações nulas (por exemplo, javax.annotation.Nonnull) e tem uma opção para ter NotNull como padrão.
Motti Strom
11

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

Ophir Radnitz
fonte
7

Infelizmente, JSR 308não adicionamos mais valores que a sugestão local Não Nula do projeto aqui

Java 8não virá com uma única anotação padrão ou sua própria Checkerestrutura. Semelhante ao Find-bugs ou JSR 305, esse JSR é mal mantido por um pequeno grupo de equipes principalmente acadêmicas.

Nenhum poder comercial por trás dele, portanto, é JSR 308lançado EDR 3(Early Draft Review às JCP) NOW, enquanto Java 8é esperado que ele seja entregue em menos de 6 meses: -O Similar ao 310btw. mas, ao contrário 308 Oracle, se encarregou disso agora de seus fundadores para minimizar os danos causados ​​à plataforma Java.

Todo projeto, fornecedor e classe acadêmica, como os que estão por trás Checker Frameworke JSR 308criarão sua própria anotação de verificador proprietário.

Tornar o código fonte incompatível nos próximos anos, até que alguns compromissos populares possam ser encontrados e talvez adicionados a Java 9ou 10, ou através de estruturas como Apache Commonsou Google Guava;-)

Werner Keil
fonte
7

Android

Esta resposta é específica para Android. O Android possui um pacote de suporte chamado support-annotations. Isto fornece dezenas de específicas Android anotações e também fornece mais comuns como NonNull, Nullableetc.

Para adicionar um 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 então use:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}
Shubham Chaudhary
fonte
5

Enquanto espera que isso seja resolvido a montante (Java 8?), Você também pode definir seu próprio projeto-local @NotNulle @Nullableanotações. Isso pode ser útil também no caso de você estar trabalhando com o Java SE, onde javax.validation.constraints não está disponível por padrão.

import java.lang.annotation.*;

/**
 * Designates that a field, return value, argument, or variable is
 * guaranteed to be non-null.
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
@Documented
@Retention(RetentionPolicy.CLASS)
public @interface NotNull {}

/**
 * Designates that a field, return value, argument, or variable may be null.
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
@Documented
@Retention(RetentionPolicy.CLASS)
public @interface Nullable {}

É certo que isso seria em grande parte para fins decorativos ou à prova de futuro, uma vez que o exposto acima obviamente não adiciona nenhum suporte à análise estática dessas anotações.

Arto Bendiken
fonte
4

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

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

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

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

Eu uso:

  1. Acesso nulo ao ponteiro: Erro
  2. Violação de especificação nula: Erro (vinculado ao ponto # 1)
  3. Acesso potencial a ponteiro nulo: Aviso (caso contrário, o SDK do facebook teria avisos)
  4. Conflito entre anotações nulas e inferência nula: Aviso (vinculado ao ponto # 3)
chaqke
fonte
4
amarrado ao Eclipse? Não é verdade.
Dcow 12/08/13
1
O @DavidCowden IntelliJ IDEA, com suporte ao desenvolvimento do Android, acho, já estava disponível algum tempo antes da invasão do AndroidStudio.
Mārtiņš Briedis 13/10
@ MārtiņšBriedis sim, isso é verdade. Eu acho que você quis dizer @chaqke.
dcow
vale a pena notar que android e intellij têm anotações separadas e provavelmente permanecerão assim até que o java inclua anotações oficiais. estas são instruções para usar as anotações do eclipse com o eclipse.
chaqke
Nunca foi vinculado ao Eclipse. Você pode usar qualquer IDE que desejar.
precisa saber é o seguinte
4

Se você estiver trabalhando em um grande projeto, talvez seja melhor criar suas próprias @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 . Desse ponto de vista, é apenas uma coisa interna .

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

  • É uma coisa interna. (sem impacto funcional ou técnico)
  • Com muitos muitos usos.
  • IDEs como o IntelliJ suportam anotações @Nullable/ personalizadas @NotNull.
  • 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 ... configurar anotações

bvdb
fonte
1
Eu tentei o seu conselho, mas ideanão conte nada sobre void test(@NonNull String s) {}chamado portest(null);
user1244932 3/17
3
@ user1244932 Você quer dizer IntelliJ IDEA? Você pode configurar as anotações de nulidade que ele usa para análise estática. Não sei exatamente onde, mas um local para defini-los é em "Arquivo> Configurações> Compilação, Execução, Implantação> Compilador" e existe um botão "Configurar anotações ...".
Adowrath
@ user1244932 veja a captura de tela se você ainda estiver procurando por isso.
bvdb
3

Já existem muitas respostas aqui, mas (a) é 2019 e ainda não há "padrão" Nullablee (b) nenhuma outra resposta faz referência ao Kotlin.

A referência ao Kotlin é importante, porque o Kotlin é 100% interoperável com Java e possui um recurso básico de Segurança Nula. Ao chamar as bibliotecas Java, é possível aproveitar essas anotações para informar às ferramentas do Kotlin se uma API Java pode aceitar ou retornar null.

Até onde eu sei, os únicos Nullablepacotes compatíveis com o Kotlin são org.jetbrains.annotationse android.support.annotation(agora androidx.annotation). O último é compatível apenas com o Android, portanto não pode ser usado em projetos JVM / Java / Kotlin que não sejam do Android. No entanto, o pacote JetBrains funciona em qualquer lugar.

Portanto, se você desenvolver pacotes Java que também funcionem no Android e Kotlin (e sejam suportados pelo Android Studio e IntelliJ), sua melhor opção provavelmente será o pacote JetBrains.

Maven:

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

Gradle:

implementation 'org.jetbrains:annotations-java5:15.0'
noamtm
fonte
2
Hmm, isso diz o contrário: kotlinlang.org/docs/reference/…
skagedal
3

Há outra maneira de fazer isso no Java 8. Estou fazendo duas coisas para realizar o que precisava:

  1. Tornando os campos anuláveis ​​explícitos aos tipos, agrupando os campos anuláveis ​​com java.util.Optional
  2. Verificando se todos os campos não anuláveis ​​não são nulos no tempo de construção com java.util.Objects.requireNonNull

Exemplo:

import static java.util.Objects.requireNonNull;

public class Role {

  private final UUID guid;
  private final String domain;
  private final String name;
  private final Optional<String> description;

  public Role(UUID guid, String domain, String name, Optional<String> description) {
    this.guid = requireNonNull(guid);
    this.domain = requireNonNull(domain);
    this.name = requireNonNull(name);
    this.description = requireNonNull(description);
  }

Então, minha pergunta é: precisamos fazer anotações ao usar o java 8?

Edit: Descobri mais tarde que alguns consideram uma prática ruim usar Optionalem argumentos, há uma boa discussão com prós e contras aqui. Por que o Java 8's Optional não deve ser usado em argumentos

Opção alternativa, considerando que não é recomendado usar Opcional nos argumentos, precisamos de 2 construtores:

  //Non null description
  public Role(UUID guid, String domain, String name, String description) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);

        // description will never be null
        requireNonNull(description);

        // but wrapped with an Optional
        this.description = Optional.of(description);
      }

  // Null description is assigned to Optional.empty
  public Role(UUID guid, String domain, String name) {
        this.guid = requireNonNull(guid);
        this.domain = requireNonNull(domain);
        this.name = requireNonNull(name);
        this.description = Optional.empty();
      }
Mozart Brocchini
fonte
Eu diria que você ainda precisa da anotação @NotNull para todos os quatro parâmetros formais, para que os verificadores de análise estática saibam sua intenção de que nenhum deve ser nulo. Ainda não há nada na linguagem Java que imponha isso. Você também deve verificar se a descrição não é nula se estiver programando defensivamente.
Jaxzin # 11/16
2
Eu ainda posso escrever este código: new Role(null,null,null,null);. Com as anotações, meu IDE e análise estática alertam que nulo não pode ser passado para esses parâmetros. Sem ele, não descubro até executar o código. Esse é o valor das anotações.
Jaxzin
2
Também estou em ambientes em que os desenvolvedores podem usar qualquer IDE ou editor de texto que preferirem, não é mutuamente exclusivo. Em seguida, também integramos o maven-pmd-plugin e / ou o SonarQube no processo de compilação para incentivar e destacar e até bloquear os problemas de qualidade de código pré-mesclados, por exemplo, em solicitações pull.
jaxzin
2
Opcional não deve ser usado como argumento de método ou campo privado. Veja por exemplo: stuartmarks.wordpress.com/2016/09/27/vjug24-session-on-optional
assylias
1
@assylias sim, descobri isso mais tarde, eles dizem que não é recomendado porque não vai nos comprar nada, eu definitivamente consigo entender o racional deles. Nesse caso, eu coloquei aqui, pode-se tornar o argumento description nulo e o código do cliente pode passar uma String vazia, mas em muitos casos pode ser útil distinguir entre String vazia e não ter valor. Obrigado por seu comentário. Vou atualizar a resposta.
Mozart Brocchini
2

O sol não tem o seu agora? O que é isso:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

Isso parece estar incluído em todas as versões do Java que usei nos últimos anos.

Editar: Como mencionado nos comentários abaixo, você provavelmente não deseja usá-los. Nesse caso, meu voto é para as anotações do IntelliJ jetbrains!

Nate W.
fonte
10
Eu não tenho idéia do que é, mas o nome do pacote deve ser uma GRANDE PISTA de que NÃO se destina ao uso geral.
Stephen C
3
Geralmente, evita-se o uso de classes no namespace com.sun, pois são internas; não destinado ao uso direto; e sem garantias quanto à sua disponibilidade ou comportamento futuro. É preciso ter um caso realmente sólido para usar diretamente um artefato com.sun.
Luis.espinal
mais alguma coisa exibida nesse formato HTML pobres (em Java2s.com ainda por cima) deve dar-lhe algumas bandeiras vermelhas :)
luis.espinal
2

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

MiguelMunoz
fonte
1

Outra opção são as anotações fornecidas com o ANTLR 4. Após a solicitação de recebimento nº 434 , o artefato que contém as anotações @NotNulle @Nullableinclui um processador de anotações que produz erros e / ou avisos em tempo de compilação no caso de um desses atributos ser mal utilizado (por exemplo, se ambos são aplicados ao mesmo item ou se @Nullableforem aplicados ao item com um tipo primitivo). O processador de anotação 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 nos casos de herança de método.

Sam Harwell
fonte
1

Se você estiver construindo seu aplicativo usando o Spring Framework, sugiro o uso javax.validation.constraints.NotNullda validação do Beans empacotada na seguinte dependência:

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>

A principal vantagem desta anotação é que o Spring fornece suporte para parâmetros de método e campos de classe anotados javax.validation.constraints.NotNull. Tudo o que você precisa fazer para ativar o suporte é:

  1. forneça o jar api para validação de beans e jar com a implementação do validador das anotações jsr-303 / jsr-349 (que vem com a dependência do Hibernate Validator 5.x):

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.4.1.Final</version>
    </dependency>
  2. forneça MethodValidationPostProcessor ao contexto de spring

      @Configuration
      @ValidationConfig
      public class ValidationConfig implements MyService {
    
            @Bean
            public MethodValidationPostProcessor providePostProcessor() {
                  return new MethodValidationPostProcessor()
            }
      }
  3. finalmente, você anota suas aulas com o Spring org.springframework.validation.annotation.Validatede a validação será automaticamente tratada pelo Spring.

Exemplo:

@Service
@Validated
public class MyServiceImpl implements MyService {

  @Override
  public Something doSomething(@NotNull String myParameter) {
        // No need to do something like assert myParameter != null  
  }
}

Quando você tenta chamar o método doSomething e passa null como o valor do parâmetro, a mola (por meio do HibernateValidator) será lançada ConstraintViolationException. Não há necessidade de trabalho manual aqui.

Você também pode validar valores de retorno.

Outro benefício importante de javax.validation.constraints.NotNullvir para o Beans Validation Framework é que, no momento, ele ainda está desenvolvido e novos recursos estão planejados para a nova versão 2.0.

Que tal @Nullable? Não há nada parecido na Validação de Beans 1.1. Bem, eu poderia argumentar que, se você decidir usar, @NotNulltudo o que NÃO é anotado @NonNullé efetivamente "anulável", portanto a @Nullableanotação é inútil.

walkeros
fonte
1
Por favor não use. É usado para validação de tempo de execução, NÃO para análise de código estático. Consulte justsomejavaguy.blogspot.com/2011/08/… para obter detalhes. Fonte: DELETED resposta com 219 votos por @ luis.espinal.
koppor 29/03
@koppor: Eu discordo. Se isso não se destina ao uso, por que o Spring o trataria em tempo de execução. Além disso, a estrutura de validação do Beans permite criar anotações apenas para análise em tempo de execução, pois permite acessar o objeto Context (instância anotada / validada atualmente) em tempo de execução.
walkeros 6/04
0

A primavera 5 tem @NonNullApi no nível do pacote. Parece uma escolha conveniente para um projeto que já possui dependências do Spring. Todos os campos, parâmetros e valores de retorno padrão para @NonNull e @Nullable podem ser aplicados nos poucos locais que diferem.

Arquivo package-info.java:

@org.springframework.lang.NonNullApi
package com.acme;

https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.nullability.annotations

ken jarrad
fonte