Frage Welche @NotNull Java Annotation sollte ich verwenden?


Ich versuche, meinen Code lesbarer zu machen und Werkzeuge wie IDE-Code-Inspektion und / oder statische Code-Analyse (Findbugs und Sonar) zu verwenden, um NullPointerExceptions zu vermeiden. Viele der Tools scheinen nicht miteinander kompatibel zu sein. @NotNull/@NonNull/@Nonnull Annotation und Auflistung aller in meinem Code wäre schrecklich zu lesen. Irgendwelche Vorschläge, welcher der Beste ist? Hier ist die Liste der äquivalenten Anmerkungen, die ich gefunden habe:

  • javax.validation.constraints.NotNull
    Erstellt für die Laufzeitvalidierung, nicht für statische Analysen.
    Dokumentation

  • edu.umd.cs.findbugs.annotations.NonNull
    Benutzt von Findbugs statische Analyse und daher Sonar (jetzt Sonarqube)
    Dokumentation

  • javax.annotation.Nonnull
    Dies könnte auch mit Findbugs funktionieren, aber JSR-305 ist inaktiv. (Siehe auch: Was ist der Status von JSR 305?) Quelle

  • org.jetbrains.annotations.NotNull
    Wird von IntelliJ IDEA IDE für die statische Analyse verwendet.
    Dokumentation

  • lombok.NonNull
    Wird verwendet, um die Code-Generierung zu steuern Projekt Lombok.
    Platzhalteranmerkung, da kein Standard vorhanden ist.
    Quelle, Dokumentation

  • android.support.annotation.NonNull
    Marker Annotation verfügbar in Android, bereitgestellt von support-annotations-Paket
    Dokumentation

  • org.eclipse.jdt.annotation.NonNull
    Wird von Eclipse zur statischen Codeanalyse verwendet
    Dokumentation


739
2018-02-10 22:05


Ursprung


Antworten:


Ich mag das sehr Checker-RahmenwerkDies ist eine Implementierung von Typ Anmerkungen (JSR-308), mit dem Fehlerprüfer wie ein Nullheitsprüfer implementiert werden. Ich habe nicht wirklich versucht, andere zu vergleichen, aber ich war mit dieser Implementierung zufrieden.

Ich bin nicht mit der Gruppe verbunden, die die Software anbietet, aber ich bin ein Fan.

Vier Dinge, die ich an diesem System mag:

  1. Es hat einen Fehlerprüfer für Nullheit (@Nullable), hat aber auch solche für Unveränderlichkeit und Praktikum (und andere). Ich benutze die erste (Nullheit) und ich versuche, die zweite (Unveränderlichkeit / IGJ) zu verwenden. Ich probiere das dritte aus, aber ich bin mir nicht sicher, ob ich es schon lange benutzen kann. Ich bin nicht von der allgemeinen Nützlichkeit der anderen Checker überzeugt, aber es ist schön zu wissen, dass das Framework selbst ein System zur Implementierung einer Vielzahl von zusätzlichen Annotationen und Checkern ist.

  2. Das Standardeinstellung für die Nullheitsüberprüfung funktioniert gut: Nicht null außer Einheimischen (NNEL). Grundsätzlich bedeutet dies, dass der Checker standardmäßig (mit Instanzvariablen, Methodenparametern, generischen Typen usw.) nur lokale Variablen behandelt, als ob sie standardmäßig den Typ "@NonNull" hätten. Per der Dokumentation:

    Der NNEL-Standardwert führt zu der geringsten Anzahl an expliziten Anmerkungen in Ihrem Code.

    Sie können einen anderen Standard für eine Klasse oder für eine Methode festlegen, wenn NNEL nicht für Sie funktioniert.

  3. Dieses Framework ermöglicht Ihnen die Verwendung mit ohne eine Abhängigkeit vom Framework zu schaffen indem Sie Ihre Anmerkungen in einen Kommentar einschließen: z.B. /*@Nullable*/. Dies ist nützlich, da Sie eine Bibliothek oder einen freigegebenen Code mit Anmerkungen versehen und überprüfen können, diese Bibliothek jedoch weiterhin in einem anderen Projekt, das das Framework nicht verwendet, verwenden können. Das ist eine nette Eigenschaft. Ich habe mich daran gewöhnt, es zu benutzen, obwohl ich dazu tendiere, das Checker-Framework jetzt für alle meine Projekte zu aktivieren.

  4. Der Rahmen hat einen Weg zu Kommentieren Sie APIs Sie verwenden, die nicht bereits mit Stub-Dateien für Ungültigkeit annotiert sind.


78
2018-02-10 22:59



Seit Oracle beschlossen, nicht zu standardisieren @NonNull (und @Nullable) für den Moment, ich fürchte, es gibt keine gute Antwort. Alles, was wir tun können, ist eine pragmatische Lösung zu finden, und meine ist wie folgt:

Syntax

Von einem rein stilistischen Standpunkt aus möchte ich jeden Bezug auf IDE, Framework oder irgendein Toolkit außer Java selbst vermeiden.

Dies schließt aus:

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

Was uns mit javax.validation.constraints oder javax.annotation verlässt. Ersteres kommt mit JEE. Wenn das besser als javax.annotation ist, was eventuell mit JSE oder gar nicht kommen wird, ist das eine Frage der Debatte. Ich persönlich bevorzuge javax.annotation, weil mir die JEE-Abhängigkeit nicht gefallen würde.

Das lässt uns mit

javax.annotation

das ist auch das kürzeste.

Es gibt nur eine Syntax, die sogar besser wäre: java.annotation.Nullable. Wie andere Pakete absolvierten von javax zu java in der vergangenheit würde die javax.annotation ein Schritt in die richtige Richtung sein.

Implementierung

Ich hatte gehofft, dass sie alle die gleiche triviale Implementierung haben, aber eine detaillierte Analyse zeigte, dass dies nicht wahr ist.

Zuerst für die Ähnlichkeiten:

Die @NonNull-Annotationen haben alle die Zeile

public @interface NonNull {}

ausser für

  • org.jetbrains.annotations, das es @NotNull aufruft und eine triviale Implementierung hat
  • javax.annotation, die eine längere Implementierung hat
  • javax.validation.constraints, die es auch @NotNull nennt und eine Implementierung hat

Die @Nullable-Annotationen haben alle die Zeile

public @interface Nullable {}

außer für (wieder) die org.jetbrains.annotations mit ihrer trivialen Implementierung.

Für die Unterschiede:

Ein auffallender ist das

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

Alle haben Runtime-Annotationen (@Retention (RUNTIME), while)

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

sind nur Kompilierzeit (@Retention (CLASS)).

Wie beschrieben in diese SO Antwort die Auswirkung von Laufzeitanmerkungen ist kleiner als man meinen könnte, aber sie haben den Vorteil Aktivieren von Werkzeugen, um zusätzlich zu den Laufzeitprüfungen weitere Prüfungen durchzuführen Kompiliere mal.

Ein weiterer wichtiger Unterschied ist woher Im Code können die Anmerkungen verwendet werden. Es gibt zwei verschiedene Ansätze. Einige Pakete verwenden JLS 9.6.4.1 Style-Kontexte. Die folgende Tabelle gibt einen Überblick:


                                FELDVERFAHREN PARAMETER LOCAL_VARIABLE
android.support.annotation X X X
edu.umd.cs.findbugs.annotations X X X X
org.jetbrains.annotation X X X X
Lombok X X X X
javax.validation.constraints X X X

org.eclipse.jdt.annotation, javax.annotation und org.checkerframework.checker.nullness.qual verwenden die in. definierten Kontexte JLS 4.11, was meiner Meinung nach der richtige Weg ist.

Das lässt uns mit

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

in dieser Runde.

Code

Um Ihnen zu helfen, weitere Details selbst zu vergleichen, liste ich den Code jeder Anmerkung unten auf. Um Vergleiche zu erleichtern, entfernte ich Kommentare, Importe und die @Dokumentierte Annotation. (Sie hatten alle @Dokumentiert, außer für die Klassen aus dem Android-Paket). Ich habe die Zeilen und @Target-Felder neu geordnet und die Qualifikationen normalisiert.

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

Der Vollständigkeit halber finden Sie hier die @Nullable-Implementierungen:

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

Die folgenden zwei Pakete haben kein @Nullable, also listet ich sie getrennt auf Lombok hat eine ziemlich langweilige @NonNull. In javax.validation.constraints ist @NonNull eigentlich ein @NotNull und es hat eine langwierige Umsetzung.

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

Unterstützung

Form my experience javax.annotation wird von Eclipse und dem Checker Framework standardmäßig unterstützt.

Zusammenfassung

Meine ideale Annotation wäre die java.annotation-Syntax mit der Checker Framework-Implementierung.

Wenn Sie nicht beabsichtigen, das Checker Framework zu verwenden, javax.annotation (JSR-305) ist vorerst noch die beste Wahl.

Wenn Sie bereit sind, in das Checker Framework einzukaufen, verwenden Sie einfach ihre org.checkerframework.checker.nullness.qual.


Quellen

  • android.support.annotation von android-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotations von findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotation aus org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotations from jetbrains-annotations-13.0.jar
  • javax.annotation von gwt-dev-2.5.1-sources.jar
  • org.checkerframework.checker.nullness.qual aus checker-framework-2.1.9.zip
  • Lombok von Lombok commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints von validation-api-1.0.0.GA-sources.jar

60
2018-03-09 12:20



Ich benutze das IntelliJ, weil ich mich hauptsächlich damit beschäftige, dass IntelliJ Dinge markiert, die eine NPE erzeugen könnten. Ich stimme zu, dass es frustrierend ist, keine Standardannotation im JDK zu haben. Es ist die Rede davon, es hinzuzufügen, es könnte es in Java 7 schaffen. In diesem Fall wird es noch eine weitere zur Auswahl geben!


49
2018-02-10 22:09



Entsprechend der Java 7-Funktionsliste JSR-308-Typ-Annotationen sind auf Java 8 zurückgestellt. JSR-305-Annotationen werden nicht einmal erwähnt.

Es gibt ein paar Informationen über den Status von JSR-305 in einem Blinddarm des letzten JSR-308-Entwurfs. Dies schließt die Beobachtung ein, dass JSR-305-Anmerkungen anscheinend aufgegeben werden. Die JSR-305-Seite zeigt es auch als "inaktiv" an.

In der Zwischenzeit besteht die pragmatische Antwort darin, die Annotationstypen zu verwenden, die von den am häufigsten verwendeten Tools unterstützt werden ... und bereit zu sein, sie zu ändern, wenn sich die Situation ändert.


In der Tat definiert JSR-308 keine Annotationstypen / -klassen und es sieht so aus, als ob sie denken, dass sie außerhalb des Bereichs liegen. (Und sie haben Recht, angesichts der Existenz von JSR-305).

Wenn JSR-308 wirklich so aussieht, als würde man es in Java 8 einbauen, würde es mich nicht wundern, wenn das Interesse an JSR-305 wiederbelebt würde. AFAIK, das JSR-305-Team hat ihre Arbeit nicht offiziell aufgegeben. Sie waren nur für 2+ Jahre ruhig.

Es ist interessant, dass Bill Pugh (der technische Leiter für JSR-305) einer der Leute hinter FindBugs ist.


31
2018-02-10 22:21



Für Android-Projekte sollten Sie verwenden android.support.annotation.NonNull und android.support.annotation.Nullable. Diese und andere hilfreiche Android-spezifische Anmerkungen sind in der Unterstützungsbibliothek.

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

Die Support-Bibliothek selbst wurde ebenfalls mit diesen Anmerkungen versehen   Anmerkungen, so wie ein Benutzer der Support-Bibliothek Android Studio wird   Überprüfen Sie Ihren Code und kennzeichnen Sie mögliche Probleme, die auf diesen basieren   Anmerkungen.


25
2017-09-05 09:51



JSR305 und FindBugs werden von derselben Person erstellt. Beide werden schlecht gewartet, sind jedoch so standard wie es geht und werden von allen wichtigen IDEs unterstützt. Die gute Nachricht ist, dass sie gut funktionieren.

Hier erfahren Sie, wie Sie @Nonnull standardmäßig auf alle Klassen, Methoden und Felder anwenden. Sehen https://stackoverflow.com/a/13319541/14731 und https://stackoverflow.com/a/9256595/14731

  1. Definieren @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. Fügen Sie die Annotation zu jedem Paket hinzu: package-info.java

@NotNullByDefault
package com.example.foo;

AKTUALISIEREN: Ab dem 12. Dezember 2012 JSR 305 wird als "ruhend" aufgeführt. Laut der Dokumentation:

Ein JSR, der vom Exekutivkomitee als "ruhend" gewählt wurde oder das Ende seiner natürlichen Lebensspanne erreicht hat.

Es sieht aus wie JSR 308  ist es in JDK 8 zu machen und obwohl der JSR @NotNull nicht definiert, das begleitende Checkers Framework tut. Zum Zeitpunkt dieses Schreibens ist das Maven-Plugin aufgrund dieses Fehlers unbrauchbar: https://github.com/typetools/checker-framework/issues/183


16
2017-11-23 02:52



Wenn jemand nur nach den IntelliJ-Klassen sucht: Sie können sie aus dem Maven-Repository mit abrufen

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

16
2018-04-25 14:15



Eclipse hat auch eigene Anmerkungen.

org.eclipse.jdt.annotation.NonNull

Siehe unter http://wiki.eclipse.org/JDT_Core/Null_Analysis für Details.


11
2017-09-17 11:10