Quale annotazione Java @NotNull dovrei usare?


999

Sto cercando di rendere il mio codice più leggibile e utilizzare strumenti come l'ispezione del codice IDE e / o l'analisi del codice statico (FindBugs e Sonar) per evitare NullPointerExceptions. Molti degli strumenti sembrano incompatibili l'uno con l'altro @NotNull/ @NonNull/ @Nonnullannotazione e elencarli tutti nel mio codice sarebbe terribile da leggere. Qualche suggerimento su quale sia il "migliore"? Ecco l'elenco delle annotazioni equivalenti che ho trovato:

  • javax.validation.constraints.NotNull
    Creato per la convalida del runtime, non per l'analisi statica.
    documentazione

  • edu.umd.cs.findbugs.annotations.NonNull
    Utilizzato da Findbugs l'analisi statica e quindi Sonar (ora Sonarqube )
    documentazione

  • javax.annotation.Nonnull
    Questo potrebbe funzionare anche con Findbugs, ma JSR-305 è inattivo. (Vedi anche: Qual è lo stato di JSR 305? ) Fonte

  • org.jetbrains.annotations.NotNull
    Utilizzato da IntelliJ IDEA IDE per analisi statiche.
    documentazione

  • lombok.NonNull
    Utilizzato per controllare la generazione del codice in Project Lombok .
    Annotazione segnaposto poiché non esiste uno standard.
    fonte , documentazione

  • android.support.annotation.NonNull
    Annotazione marker disponibile in Android, fornita dalla documentazione del pacchetto di annotazioni di supporto

  • org.eclipse.jdt.annotation.NonNull
    Utilizzato da Eclipse per la documentazione di analisi del codice statico


203
apache dovrebbe inventare un'annotazione "comune" e uno strumento in grado di convertirla in qualsiasi altra annotazione. la soluzione al problema di troppi standard è inventare un nuovo standard.
irreversibile

6
@irreputable se apache inventa un nuovo "comune", ne esisterebbero 56 versioni, sovrapposte ad altri progetti. E non sarebbe comunque standard (standard! = Diffuso). Meglio usare qualcosa di veramente standard, javax?. *. A proposito, non ci sono "troppi standard" in quegli esempi, vedo solo 1 o 2.
ymajoros,

6
javax.annotation.Nonnull funziona con findbugs (appena testato), che è una ragione convincente per me per usarlo.
Nicolas C,

20
Se scrivo semplicemente @NotNull, si riferisce a com.sun.istack.internal.NotNull. OMG ...
Thomas Weller,

3
@MozartBrocchini - Gli optionals sono utili nei casi in cui potresti aver precedentemente utilizzato NullObjects. In realtà non si rivolgono allo stesso obiettivo di un'annotazione runtime \ @NotNull e introducono noioso da scartare.
Dave,

Risposte:


205

Dal momento che JSR 305 (il cui obiettivo era standardizzare @NonNulle @Nullable) è rimasto inattivo per diversi anni, temo che non ci sia una buona risposta. Tutto quello che possiamo fare è trovare una soluzione pragmatica e la mia è la seguente:

Sintassi

Da un punto di vista puramente stilistico, vorrei evitare qualsiasi riferimento a IDE, framework o toolkit tranne Java stesso.

Questo esclude:

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

Che ci lascia con javax.validation.constraintso javax.annotation. Il primo viene fornito con JEE. Se questo è meglio di javax.annotation, che potrebbe venire alla fine con JSE o mai del tutto, è una questione di dibattito. Personalmente preferisco javax.annotationperché non mi piacerebbe la dipendenza JEE.

Questo ci lascia con

javax.annotation

che è anche il più corto.

Esiste una sola sintassi che sarebbe ancora meglio: java.annotation.Nullable. Man mano che altri pacchetti passavano da javaxa javain passato, javax.annotation sarebbe un passo nella giusta direzione.

Implementazione

Speravo che tutti abbiano sostanzialmente la stessa banale implementazione, ma un'analisi dettagliata ha dimostrato che ciò non è vero.

Innanzitutto per le somiglianze:

Le @NonNullannotazioni hanno tutte la linea

public @interface NonNull {}

eccetto per

  • org.jetbrains.annotationsche lo chiama @NotNulle ha un'implementazione banale
  • javax.annotation che ha un'implementazione più lunga
  • javax.validation.constraintsche lo chiama anche @NotNulle ha un'implementazione

Le @Nullableannotazioni hanno tutte la linea

public @interface Nullable {}

fatta eccezione per (di nuovo) i org.jetbrains.annotationscon la loro banale implementazione.

Per le differenze:

Quello che colpisce è quello

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

tutti hanno annotazioni di runtime ( @Retention(RUNTIME)), mentre

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

sono solo tempi di compilazione ( @Retention(CLASS)).

Come descritto in questa risposta SO, l'impatto delle annotazioni di runtime è minore di quanto si possa pensare, ma hanno il vantaggio di consentire agli strumenti di eseguire controlli di runtime oltre a quelli di compilazione.

Un'altra differenza importante è dove nel codice possono essere usate le annotazioni. Esistono due approcci diversi. Alcuni pacchetti usano contesti di stile JLS 9.6.4.1. La tabella seguente offre una panoramica:

                                PARAMETRO METODO DI 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.qualutilizzare i contesti definiti JLS 4.11, che è a mio parere il modo giusto per farlo.

Questo ci lascia con

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

in questo round.

Codice

Per aiutarti a confrontare ulteriori dettagli da solo, elencherò il codice di ogni annotazione qui sotto. Per facilitare il confronto ho rimosso commenti, importazioni e @Documentedannotazioni. (avevano tutti @Documentedtranne le lezioni dal pacchetto Android). Ho riordinato le linee e i @Targetcampi e normalizzato le qualifiche.

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

Per completezza, ecco le @Nullableimplementazioni:

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

I seguenti due pacchetti non hanno @Nullable, quindi li elenco separatamente; Lombok ha un aspetto piuttosto noioso @NonNull. In javax.validation.constraintsthe @NonNullis a @NotNull e ha un'implementazione longish.

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();
    }
}

Supporto

Dalla mia esperienza, javax.annotationè almeno supportato da Eclipse e Checker Framework immediatamente.

Sommario

La mia annotazione ideale sarebbe la java.annotationsintassi con l'implementazione di Checker Framework.

Se non si intende utilizzare il Checker Framework javax.annotation( JSR-305 ) è ancora la soluzione migliore per il momento.

Se sei disposto ad acquistare nel Checker Framework usa semplicemente il loro org.checkerframework.checker.nullness.qual.


fonti

  • android.support.annotation a partire dal android-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotations a partire dal findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotation a partire dal org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotations a partire dal jetbrains-annotations-13.0.jar
  • javax.annotation a partire dal gwt-dev-2.5.1-sources.jar
  • org.checkerframework.checker.nullness.qual a partire dal checker-framework-2.1.9.zip
  • lombok a partire dal lombok commitf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints a partire dal validation-api-1.0.0.GA-sources.jar

7
Il rovescio della medaglia di javax.annotationè che è a) basato su un JSR morto, b) difficile trovare un artefatto che fornisce solo le annotazioni e viene mantenuto. Quello di findbugs non è: search.maven.org/…
robinst

18
Un altro punto contrario javax.annotationè che causa problemi con Java 9 perché anche altri moduli forniscono classi in quel pacchetto (jax-ws).
robinst,

10
@kevinarpe: il progetto Findbugs è morto e il progetto successivo Spotbugs sta rimuovendo quelle annotazioni: github.com/spotbugs/spotbugs/pull/180
robinst,

4
JSR 305 , che sarebbe stato standardizzato javax.annotation.NonNull, non è mai stato completato perché il suo vantaggio specifico è andato AWOL. Non aveva nulla a che fare con alcuna decisione di Oracle.
Mark Reinhold,

5
Un altro motivo per non usare jsr305.jar è che apparentemente viola la licenza binaria Oracle Java: github.com/google/guava/issues/2960
Flow

91

Mi piace molto il Checker Framework , che è un'implementazione di annotazioni di tipo ( JSR-308 ) che viene utilizzata per implementare i controlli dei difetti come un controllo di nullità. Non ho mai provato nessun altro a offrire alcun confronto, ma sono stato contento di questa implementazione.

Non sono affiliato al gruppo che offre il software, ma sono un fan.

Quattro cose che mi piacciono di questo sistema:

  1. Ha un controllo dei difetti per nullità (@Nullable), ma ha anche quelli per l' immutabilità e l' internamento (e altri). Uso il primo (nullità) e sto provando ad usare il secondo (immutabilità / IGJ). Sto provando il terzo, ma non sono ancora sicuro di usarlo a lungo termine. Non sono ancora convinto dell'utilità generale degli altri ispettori, ma è bello sapere che il framework stesso è un sistema per implementare una varietà di annotazioni e ispettori aggiuntivi.

  2. L' impostazione predefinita per il controllo della nullità funziona bene: non null tranne i locali (NNEL). Fondamentalmente questo significa che per impostazione predefinita il checker tratta tutto (variabili di istanza, parametri di metodo, tipi generici, ecc.) Ad eccezione delle variabili locali come se per impostazione predefinita abbiano un tipo @NonNull. Per la documentazione:

    L'impostazione predefinita di NNEL porta al minor numero di annotazioni esplicite nel codice.

    Puoi impostare un valore predefinito diverso per una classe o per un metodo se NNEL non funziona per te.

  3. Questo framework consente di utilizzare senza creare una dipendenza dal framework racchiudendo le annotazioni in un commento: ad es /*@Nullable*/. Questo è utile perché puoi annotare e controllare una libreria o un codice condiviso, ma puoi comunque usare quella libreria / codice condiviso in un altro progetto che non usa il framework. Questa è una bella caratteristica. Mi sono abituato ad usarlo, anche se tendo ad abilitare Checker Framework su tutti i miei progetti ora.

  4. Il framework ha un modo per annotare le API che usi che non sono già annotate per nullità usando i file stub.


3
Sembra fantastico e mi piacerebbe usarlo, ma non posso. Perché GPL Non potrebbe essere invece la LGPL?
Burkhard,

13
Secondo le FAQ : "La licenza MIT più permissiva si applica al codice che potresti voler includere nel tuo programma, come le annotazioni."
seanf

1
I collegamenti sono attualmente interrotti. Ma +1 per i consigli sull'utilizzo di Checker Framework.
Paul Wagland,

1
È un peccato che i controlli dell'immutabilità siano stati rilasciati nell'ultima versione.
Franklin Yu,

1
Checker Framework è anche suggerito in Oracle Java Tutorials .
Quazi Irfan,

55

Uso IntelliJ, perché mi occupo principalmente di IntelliJ che segnala cose che potrebbero produrre un NPE. Sono d'accordo che è frustrante non avere un'annotazione standard nel JDK. Si parla di aggiungerlo, potrebbe trasformarlo in Java 7. Nel qual caso ce ne sarà un altro tra cui scegliere!


68
Aggiornamento: IntelliJ ora supporta tutte le annotazioni di cui sopra per l'evidenziazione del codice, quindi non sei più limitato alle annotazioni di IntelliJ: blogs.jetbrains.com/idea/2011/03/…
Daniel Alexiuc,

31
E anche Eclipse Juno!
jFrenetic,

5
javax.annotation.Nonnullè più ampiamente accettato, no?
Martin,

1
@DanielAlexiuc Ma sfortunatamente, non li usa per i suoi controlli di runtime, quindi c'è ancora un vantaggio nell'uso di quelli JetBrains ...
Trejkaz,

4
@Trejkaz Dal 2016.3 crea controlli di runtime per tutti questi.
Karol S

32

Secondo l' elenco delle funzionalità di Java 7, le annotazioni di tipo JSR-308 sono rinviate a Java 8. Le annotazioni JSR-305 non vengono nemmeno menzionate.

Ci sono alcune informazioni sullo stato di JSR-305 in un'appendice dell'ultima bozza di JSR-308. Ciò include l'osservazione che le annotazioni JSR-305 sembrano essere abbandonate. La pagina JSR-305 la mostra anche come "inattiva".

Nel frattempo, la risposta pragmatica è quella di utilizzare i tipi di annotazione supportati dagli strumenti più utilizzati ... ed essere pronti a cambiarli se la situazione cambia.


In effetti, JSR-308 non definisce alcun tipo / classe di annotazione e sembra che pensino che sia fuori portata. (E hanno ragione, vista l'esistenza di JSR-305).

Tuttavia, se JSR-308 sembra davvero trasformarsi in Java 8, non mi sorprenderebbe se l'interesse per JSR-305 rianimasse. AFAIK, il team JSR-305 non ha abbandonato formalmente il proprio lavoro. Sono stati in silenzio per 2+ anni.

È interessante notare che Bill Pugh (il capo tecnologico di JSR-305) è uno dei tipi dietro FindBugs.


4
@pst: il programma attuale prevede che Java 8 venga rilasciato a settembre 2013 - infoq.com/news/2012/04/jdk-8-milestone-release-dates
Stephen C

2
Ora è scivolato a marzo 2014 - openjdk.java.net/projects/jdk8 . JSR 308 è incluso nella build M7 (consultare "104 - Annotazioni su tipi Java").
Stephen C,

28

Per i progetti Android è necessario utilizzare android.support.annotation.NonNulle android.support.annotation.Nullable. Queste e altre utili annotazioni specifiche per Android sono disponibili nella Libreria di supporto .

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

La stessa libreria di supporto è stata anche annotata con queste annotazioni, quindi come utente della libreria di supporto, Android Studio controllerà già il tuo codice e contrassegnerà potenziali problemi in base a queste annotazioni.


3
Sarebbe utile fornire una giustificazione per tale raccomandazione.
albicocca

2
tools.android.com/tech-docs/support-annotations "Anche la libreria di supporto stessa è stata annotata con queste annotazioni, quindi come utente della libreria di supporto, Android Studio controllerà già il tuo codice e contrassegnerà potenziali problemi in base a queste annotazioni ".
James Wald,

3
BTW Android Studio supporta jsr305 anche con javax.annotation.*annotazioni
CAMOBAP il

19

Se qualcuno sta solo cercando le classi IntelliJ: puoi ottenerle dal repository Maven con

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

Questo è quello che fa sì che Intellij emetta avvertimenti, sì.
Fai clic su Aggiorna

La versione attuale (al 05/2017) è il 15.0
BamaPookie

Destra. Ho aggiornato la versione. Anche se immagino che non sia cambiato molto.
Bruno Eberhard,

Tieni presente che le annotazioni JetBrains non vengono conservate per il runtime, quindi il supporto di Guice @Nullable, ad esempio, non funziona con esso.
Peter Major,

18

JSR305 e FindBugs sono creati dalla stessa persona. Entrambi sono scarsamente mantenuti, ma sono di serie e supportati da tutti i principali IDE. La buona notizia è che funzionano bene così come sono.

Ecco come applicare @Nonnull a tutte le classi, metodi e campi per impostazione predefinita. Vedi https://stackoverflow.com/a/13319541/14731 e https://stackoverflow.com/a/9256595/14731

  1. Definire @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. Aggiungi l'annotazione a ciascun pacchetto: package-info.java

@NotNullByDefault
package com.example.foo;

AGGIORNAMENTO : dal 12 dicembre 2012 JSR 305 è elencato come "inattivo". Secondo la documentazione:

Una JSR che è stata votata "inattiva" dal Comitato esecutivo o che ha raggiunto la fine della sua durata di vita naturale.

Sembra che JSR 308 lo stia trasformando in JDK 8 e sebbene JSR non definisca @NotNull, lo accompagna Checkers Framework. Al momento della stesura di questo documento, il plug-in Maven è inutilizzabile a causa di questo errore: https://github.com/typetools/checker-framework/issues/183


2
Il problema di showstopper per maven è stato risolto. Quindi questa dovrebbe essere di nuovo un'opzione.
Marc von Renteln,

Uso FindBugs tramite Maven, il mio IDE non fa nulla, questo evita le annotazioni specifiche dell'IDE, cosa consiglieresti?
Christophe Roussy,

@ChristopheRoussy La tua domanda è specifica per l'IDE. Si prega di aprire una domanda separata.
Gili,

15

Distinguere tra analisi statica e analisi di runtime. Usa l'analisi statica per cose interne e l'analisi di runtime per i confini pubblici del tuo codice.

Per cose che non dovrebbero essere nulle:

  • Verifica runtime: utilizzare "if (x == null) ..." (dipendenza zero) o @ javax.validation.NotNull (con convalida bean) o @ lombok.NonNull (semplice e semplice) o guavas Preconditions.checkNotNull (.. .)

    • Utilizzare Opzionale per i tipi di restituzione del metodo (solo). Java8 o Guava.
  • Controllo statico: utilizzare un'annotazione @NonNull

  • Dove si adatta, usa le annotazioni @ ... NonnullByDefault a livello di classe o pacchetto. Crea tu stesso queste annotazioni (gli esempi sono facili da trovare).
    • Altrimenti, utilizzare @ ... CheckForNull sul metodo restituito per evitare NPE

Ciò dovrebbe dare il miglior risultato: avvisi nell'IDE, errori di Findbugs e checkerframework, eccezioni significative di runtime.

Non aspettarti che i controlli statici siano maturi, la loro denominazione non è standardizzata e diverse librerie e IDE li trattano in modo diverso, ignorandoli. Le classi javax.annotations. * JSR305 sembrano standard, ma non lo sono e causano pacchetti divisi con Java9 +.

Alcune spiegazioni delle note:

  • Le annotazioni findbugs / spotbugs / jsr305 con il pacchetto javax.validation. * Si scontrano con altri moduli in Java9 +, probabilmente violando anche la licenza Oracle
  • Le annotazioni di Spotbugs dipendono ancora dalle annotazioni jsr305 / findbugs su compiletime (al momento della stesura di https://github.com/spotbugs/spotbugs/issues/421 )
  • jetbrains @NotNull name è in conflitto con @ javax.validation.NotNull.
  • le annotazioni jetbrains, eclipse o checkersframework per il controllo statico hanno il vantaggio rispetto a javax.annotations che non si scontrano con altri moduli in Java9 e versioni successive
  • @ javax.annotations.Nullable non significa per Findbugs / Spotbugs ciò che tu (o il tuo IDE) pensi che significhi. Findbugs lo ignorerà (sui membri). Triste, ma vero ( https://sourceforge.net/p/findbugs/bugs/1181 )
  • Per il controllo statico all'esterno di un IDE, esistono 2 strumenti gratuiti: Spotbugs (precedentemente Findbugs) e checkersframework.
  • La libreria Eclipse ha @NonNullByDefault, jsr305 ha solo @ParametersAreNonnullByDefault. Questi sono semplici wrapper di convenienza che applicano annotazioni di base a tutto in un pacchetto (o classe), che puoi facilmente creare. Questo può essere usato sul pacchetto. Ciò può essere in conflitto con il codice generato (ad esempio lombok).
  • L'uso di lombok come dipendenza esportata dovrebbe essere evitato per le librerie che condividi con altre persone, meno dipendenze transitive, meglio è
  • L'uso del framework di convalida Bean è potente, ma richiede un elevato sovraccarico, quindi è eccessivo solo per evitare il controllo null manuale.
  • L'uso di Facoltativo per i campi e i parametri del metodo è controverso (puoi trovare facilmente articoli su di esso)
  • Le annotazioni null Android fanno parte della libreria di supporto Android, vengono con molte altre classi e non giocano bene con altre annotazioni / strumenti

Prima di Java9, questa è la mia raccomandazione:

// 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);
    }
}

Si noti che non è possibile fare in modo che Spotbugs emetta un avviso quando viene indicato un parametro del metodo nullable (al momento della stesura della versione 3.1 di Spotbugs). Forse checkerframework può farlo.

Purtroppo queste annotazioni non distinguono tra i casi di un metodo pubblico di una biblioteca con callites arbitrari e metodi non pubblici in cui ogni callites può essere conosciuto. Quindi il doppio significato di: "Indicare che null è indesiderato, ma prepara comunque che null sia passato" non è possibile in una singola dichiarazione, quindi l'esempio sopra ha annotazioni diverse per l'interfaccia e l'implementazione.

Per i casi in cui l'approccio con interfaccia divisa non è pratico, il seguente approccio è un compromesso:

        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);
        }

Questo aiuta i client a non passare null (scrivendo il codice corretto), restituendo al contempo errori utili.


Ho trovato questa risposta solo ora, ma @tkruse, dove l'hai trovata: "Le annotazioni jdt di Eclipse non sono applicabili ai ritorni del metodo statico e ad altri casi"? (la prima parte non è vera, la seconda piuttosto vaga :)).
Stephan Herrmann,

@StephanHerrmann: non ricordo. Ho rimosso il punto elenco.
tkruse

12

Eclipse ha anche le sue annotazioni.

org.eclipse.jdt.annotation.NonNull

Vedi http://wiki.eclipse.org/JDT_Core/Null_Analysis per i dettagli.


Sembra che questo sarà integrato da Eclipse 3.8 (Juno) che porterà Eclipse in linea con IntelliJ a questo proposito. Inoltre, dovrebbe consentire di configurare le proprie annotazioni Null (ad esempio javax.annotation.Nonnull) e ha un'opzione per avere NotNull come predefinito.
Motti Strom,

11

Sottolineo solo che l'API di convalida Java ( javax.validation.constraints.*) non include @Nullableun'annotazione, che è molto utile in un contesto di analisi statica. Ha senso per la convalida del bean di runtime in quanto questo è il valore predefinito per qualsiasi campo non primitivo in Java (ovvero nulla da convalidare / applicare). Ai fini dichiarati ciò dovrebbe pesare verso le alternative.


7

Sfortunatamente, JSR 308non aggiungerà più valori di questo suggerimento Not Null locale qui

Java 8non verrà fornito con una singola annotazione predefinita o un proprio Checkerframework. Simile a Find-bugs o JSR 305, questo JSR è gestito male da un piccolo gruppo di squadre prevalentemente accademiche.

Nessun potere commerciale dietro di esso, quindi JSR 308lancia EDR 3(Early Draft Review a JCP) ORA, mentre Java 8dovrebbe essere spedito in meno di 6 mesi: -O Simile a 310BTW. ma a differenza 308 Oracledi quello che ora ha preso in carico dai suoi fondatori per minimizzare i danni che arrecherà alla piattaforma Java.

Ogni progetto, fornitore e classe accademica come quelli che stanno dietro Checker Frameworke JSR 308creeranno la propria annotazione di dama proprietaria.

Rendere il codice sorgente incompatibile per gli anni a venire, fino a quando alcuni compromessi popolari potrebbero essere trovati e forse aggiunti a Java 9o 10, o tramite framework come Apache Commonso Google Guava;-)


7

androide

Questa risposta è specifica per Android. Android ha un pacchetto di supporto chiamato support-annotations. Questo fornisce dozzine di annotazioni specifiche per Android e fornisce anche quelle comuni come NonNull, Nullableecc.

Per aggiungere il pacchetto support-annotations , aggiungi la seguente dipendenza nel tuo build.gradle:

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

e quindi usa:

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}

5

In attesa che questo venga risolto a monte (Java 8?), Potresti anche definire il tuo progetto locale @NotNulle le @Nullableannotazioni. Questo può essere utile anche nel caso in cui lavori con Java SE, dove javax.validation.constraints non è disponibile per impostazione predefinita.

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

Questo sarebbe certamente in gran parte per scopi decorativi o a prova di futuro, dal momento che quanto sopra ovviamente non aggiunge di per sé alcun supporto per l'analisi statica di queste annotazioni.


4

Se stai sviluppando per Android, sei in qualche modo legato a Eclipse (modifica: al momento della scrittura, non più), che ha le sue annotazioni. È incluso in Eclipse 3.8+ (Juno), ma disabilitato per impostazione predefinita.

È possibile abilitarlo in Preferenze> Java> Compilatore> Errori / Avvisi> Analisi nulla (sezione comprimibile in basso).

Seleziona "Abilita analisi null basata su annotazioni"

http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage contiene consigli sulle impostazioni. Tuttavia, se hai progetti esterni nel tuo spazio di lavoro (come Facebook SDK), potrebbero non soddisfare questi consigli e probabilmente non vorrai correggerli con ogni aggiornamento SDK ;-)

Io uso:

  1. Accesso puntatore nullo: errore
  2. Violazione della specifica nulla: errore (collegato al punto 1)
  3. Accesso al puntatore nullo potenziale: avviso (altrimenti Facebook SDK avrebbe degli avvisi)
  4. Conflitto tra annotazioni null e inferenza null: Avviso (collegato al punto 3)

4
legato a Eclipse? Non vero.
dc

1
@DavidCowden IntelliJ IDEA con supporto per gli sviluppatori Android, credo, era disponibile qualche tempo prima che AndroidStudio venisse introdotto.
Mārtiņš Briedis,

@ MārtiņšBriedis sì, è vero. Penso che volevi dire @chaqke.
dc

vale la pena notare che Android e Intellij hanno annotazioni separate e probabilmente rimarranno tali fino a quando java non includerà annotazioni ufficiali. queste sono le istruzioni per usare le annotazioni di eclipse con eclipse.
Chaqke,

Non è mai stato legato a Eclipse. Puoi usare qualsiasi IDE che desideri.
DennisK,

4

Se stai lavorando a un grande progetto, potresti essere meglio di creare il tuo @Nullable e / o @NotNullannotazioni.

Per esempio:

@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 si utilizza il criterio di conservazione corretto , le annotazioni non saranno disponibili in fase di esecuzione . Da quel punto di vista, è solo una cosa interna .

Anche se questa non è una scienza rigorosa, penso che abbia molto senso usare una classe interna per essa.

  • È una cosa interna. (nessun impatto funzionale o tecnico)
  • Con molti molti molti usi.
  • Gli IDE come IntelliJ supportano personalizzazioni @Nullable/ @NotNullannotazioni.
  • La maggior parte dei framework preferisce utilizzare anche la propria versione interna.

Domande aggiuntive (vedi commenti):

Come configurarlo in IntelliJ?

Fai clic sul "funzionario di polizia" nell'angolo in basso a destra della barra di stato di IntelliJ. E fai clic su "Configura ispezioni" nel popup. Il prossimo ... configurare le annotazioni


1
Ho provato il tuo consiglio, ma ideanon ho parlato di void test(@NonNull String s) {}chiamato datest(null);
user1244932

3
@ user1244932 Intendi IntelliJ IDEA? È possibile configurare le annotazioni di nullability che utilizza per l'analisi statica. Non so esattamente dove, ma un posto in cui definirli è in "File> Impostazioni> Build, Execution, Deployment> Compiler" e c'è un pulsante "Configura annotazioni ...".
Adowrath,

@ user1244932 vedi screenshot se lo stai ancora cercando.
bvdb,

3

Ci sono già troppe risposte qui, ma (a) è il 2019, e non c'è ancora "standard" Nullablee (b) nessun'altra risposta fa riferimento a Kotlin.

Il riferimento a Kotlin è importante, perché Kotlin è interoperabile al 100% con Java e ha una funzionalità Null Safety di base. Quando si chiamano le librerie Java, è possibile sfruttare queste annotazioni per far sapere agli strumenti Kotlin se un'API Java può accettare o restituire null.

Per quanto ne so, gli unici Nullablepacchetti compatibili con Kotlin sono org.jetbrains.annotationse android.support.annotation(ora androidx.annotation). Quest'ultimo è compatibile solo con Android, quindi non può essere utilizzato in progetti JVM / Java / Kotlin non Android. Tuttavia, il pacchetto JetBrains funziona ovunque.

Quindi, se sviluppi pacchetti Java che dovrebbero funzionare anche su Android e Kotlin (e supportati da Android Studio e IntelliJ), la scelta migliore è probabilmente il pacchetto JetBrains.

Esperto di:

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

Gradle:

implementation 'org.jetbrains:annotations-java5:15.0'

2
Hmm, questo dice il contrario: kotlinlang.org/docs/reference/…
skagedal

3

C'è un altro modo per farlo in Java 8. Sto facendo 2 cose per ottenere ciò di cui avevo bisogno:

  1. Rendere espliciti i campi nullable con i tipi avvolgendo i campi nullable con java.util.Optional
  2. Verifica che tutti i campi non annullabili non siano nulli in fase di costruzione con java.util.Objects.requireNonNull

Esempio:

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);
  }

Quindi la mia domanda è: dobbiamo anche annotare quando si usa Java 8?

Modifica: ho scoperto in seguito che alcuni considerano una cattiva pratica da usare Optionalnegli argomenti, c'è una buona discussione con pro e contro qui Perché Java 8's Optional non dovrebbe essere usato negli argomenti

Opzione alternativa dato che non è consigliabile utilizzare Opzionale negli argomenti, abbiamo bisogno di 2 costruttori:

  //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();
      }

Direi che hai ancora bisogno dell'annotazione @NotNull per tutti e 4 i parametri formali in modo che i controllori di analisi statiche conoscano la tua intenzione che nessuno dovrebbe essere nullo. Non c'è ancora niente nel linguaggio Java che lo imponga. Dovresti anche verificare che la descrizione non sia nulla se stai programmando in modo difensivo.
Jaxzin,

2
Posso ancora scrivere questo codice: new Role(null,null,null,null);. Con le annotazioni il mio IDE e l'analisi statica avvertiranno che non è possibile passare null a quei parametri. Senza di essa non lo scopro finché non eseguo il codice. Questo è il valore delle annotazioni.
Jaxzin,

2
Sono anche in ambienti in cui gli sviluppatori possono utilizzare qualsiasi IDE o editor di testo che preferiscono, non si escludono a vicenda. Inoltre, integriamo anche il plugin maven-pmd e / o SonarQube nel processo di compilazione per incoraggiare ed evidenziare, e persino eliminare, i problemi di qualità del codice pre-unione, ad esempio nelle richieste pull.
Jaxzin,

2
Opzionale non è pensato per essere usato come argomento di metodo o campo privato. Vedi ad esempio: stuartmarks.wordpress.com/2016/09/27/vjug24-session-on-optional
assylias

1
@assylias sì, l'ho scoperto più tardi, dicono che non è raccomandato perché non ci comprerà nulla, posso sicuramente capire il loro razionale. In questo caso ho messo qui, si potrebbe rendere l'argomento description non nullo e il codice client potrebbe passare una stringa vuota, ma in molti casi potrebbe essere utile distinguere tra stringa vuota e non avere un valore. Grazie per il tuo commento. Aggiornerò la risposta.
Mozart Brocchini,

2

Il sole non ha il suo adesso? Di cosa si tratta:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

Questo sembra essere impacchettato con tutte le versioni di Java che ho usato negli ultimi anni.

Modifica: come menzionato nei commenti qui sotto, probabilmente non vorrai usarli. In tal caso, il mio voto è per le annotazioni IntelliJ jetbrains!


10
Non ho idea di cosa sia, ma il nome del pacchetto dovrebbe essere un GRANDE CLUE che NON è destinato all'uso generale.
Stephen C,

3
Uno di solito si astiene dall'usare le classi nello spazio dei nomi com.sun in quanto interne; non destinato all'uso diretto; e senza garanzie in merito alla loro disponibilità o comportamento futuri. Bisogna avere un caso davvero solido per usare direttamente un artefatto com.sun.
luis.espinal,

inoltre qualcosa visualizzato in un formato HTML così scarso (su Java2s.com per finire) dovrebbe darti alcune bandiere rosse :)
luis.espinal,

2

Una delle cose belle di IntelliJ è che non è necessario utilizzare le loro annotazioni. Puoi scrivere il tuo o puoi usare quelli di qualunque altro strumento ti piaccia. Non sei nemmeno limitato a un solo tipo. Se si utilizzano due librerie che utilizzano diverse annotazioni @NotNull, è possibile indicare a IntelliJ di utilizzarle entrambe. Per fare ciò, vai su "Configura ispezioni", fai clic sul controllo "Condizioni costanti ed eccezioni" e premi il pulsante "Configura ispezioni". Uso Nullness Checker dove posso, quindi ho impostato IntelliJ per utilizzare quelle annotazioni, ma puoi farlo funzionare con qualsiasi altro strumento tu voglia. (Non ho opinioni sugli altri strumenti perché utilizzo le ispezioni di IntelliJ da anni e le adoro.)


1

Un'altra opzione sono le annotazioni fornite con ANTLR 4. A seguito della richiesta pull n. 434 , l'artefatto contenente le annotazioni @NotNulle @Nullableinclude un processore di annotazioni che produce errori in fase di compilazione e / o avvisi nel caso in cui uno di questi attributi venga utilizzato in modo improprio (ad esempio, se entrambi vengono applicati allo stesso oggetto o se @Nullableviene applicato a un oggetto con un tipo primitivo). Il processore di annotazione fornisce ulteriori garanzie durante il processo di sviluppo del software che le informazioni trasmesse dall'applicazione di queste annotazioni sono accurate, anche in caso di ereditarietà del metodo.


1

Se si sta costruendo l'applicazione utilizzando il framework Spring Io suggerirei di usare javax.validation.constraints.NotNullritornassi da Fagioli di convalida confezionati in seguito dipendenza:

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

Il vantaggio principale di questa annotazione è che Spring fornisce supporto sia per i parametri del metodo che per i campi di classe annotati javax.validation.constraints.NotNull. Tutto quello che devi fare per abilitare il supporto è:

  1. fornire il vaso API per la convalida dei fagioli e il vaso con l'implementazione del validatore delle annotazioni jsr-303 / jsr-349 (che viene fornito con la dipendenza 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. fornire MethodValidationPostProcessor al contesto di Spring

      @Configuration
      @ValidationConfig
      public class ValidationConfig implements MyService {
    
            @Bean
            public MethodValidationPostProcessor providePostProcessor() {
                  return new MethodValidationPostProcessor()
            }
      }
  3. infine annoti le tue lezioni con quelle di Spring org.springframework.validation.annotation.Validatede la validazione verrà gestita automaticamente da Spring.

Esempio:

@Service
@Validated
public class MyServiceImpl implements MyService {

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

Quando provi a chiamare il metodo doSomething e passi null come valore del parametro, verrà lanciato spring (tramite HibernateValidator) ConstraintViolationException. Non c'è bisogno di lavoro manuale qui.

Puoi anche validare i valori di ritorno.

Un altro importante vantaggio javax.validation.constraints.NotNullderivante dal Beans Validation Framework è che al momento è ancora sviluppato e sono previste nuove funzionalità per la nuova versione 2.0.

Che dire @Nullable? Non c'è nulla di simile in Beans Validation 1.1. Bene, potrei sostenere che se decidi di usare @NotNulltutto ciò che NON è annotato @NonNullè effettivamente "nullable", quindi l' @Nullableannotazione è inutile.


1
Per favore, non usarlo. Viene utilizzato per la convalida del runtime, NON per l'analisi del codice statico. Vedi justsomejavaguy.blogspot.com/2011/08/… per i dettagli. Fonte: risposta soppressa con 219 voti di @ luis.espinal.
Koppor

@koppor: non sono d'accordo. Se questo non è destinato all'uso, perché Spring dovrebbe gestirlo in fase di esecuzione. Inoltre, il framework di convalida Beans consente di creare annotazioni esclusivamente per l'analisi di runtime, in quanto consente di accedere all'oggetto Context (instancje attualmente annotato / convalidato) in fase di runtime.
walkeros

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.