1308 Stimmen

Welche @NotNull-Java-Annotation sollte ich verwenden?

Ich möchte meinen Code lesbarer machen und Tools wie IDE-Code-Inspektion und/oder statische Code-Analyse (FindBugs und Sonar) verwenden, um NullPointerExceptions zu vermeiden. Viele der Tools scheinen inkompatibel mit den anderen zu sein. @NotNull / @NonNull / @Nonnull Annotation und die Auflistung aller von ihnen in meinem Code wäre schrecklich zu lesen. Irgendwelche Vorschläge, welche die "beste" ist? Hier ist die Liste der gleichwertigen Annotationen, die ich gefunden habe:

416voto

Ludwig Weinzierl Punkte 14696

Desde JSR 305 (dessen Ziel die Standardisierung @NonNull y @Nullable ) seit mehreren Jahren ruht, gibt es leider keine gute Antwort. Alles, was wir tun können, ist, eine pragmatische Lösung zu finden, und meine lautet wie folgt:

Syntax

Aus rein stilistischen Gründen möchte ich jeden Verweis auf eine IDE, ein Framework oder ein 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

Damit bleibt uns entweder javax.validation.constraints o javax.annotation . Ersteres wird mit JEE geliefert. Wenn dies besser ist als javax.annotation Die Frage, ob die JSE irgendwann oder gar nicht kommen wird, ist umstritten. Ich persönlich bevorzuge javax.annotation weil mir die JEE-Abhängigkeit nicht gefallen würde.

Damit bleibt uns

javax.annotation

die auch die kürzeste ist.

Es gibt nur eine Syntax, die noch besser wäre: java.annotation.Nullable . Wie andere Pakete graduiert von javax a java in der Vergangenheit war, wäre die javax.annotation ein Schritt in die richtige Richtung sein.

Umsetzung

Ich hatte gehofft, dass sie alle im Grunde die gleiche triviale Implementierung haben, aber eine detaillierte Analyse hat gezeigt, dass dies nicht der Fall ist.

Zunächst zu den Ähnlichkeiten:

En @NonNull Anmerkungen haben alle die Zeile

public @interface NonNull {}

mit Ausnahme von

  • org.jetbrains.annotations in dem es heißt @NotNull und hat eine triviale Implementierung
  • javax.annotation die eine längere Umsetzung hat
  • javax.validation.constraints die es auch als @NotNull und hat eine Implementierung

En @Nullable Anmerkungen haben alle die Zeile

public @interface Nullable {}

mit Ausnahme (wiederum) der org.jetbrains.annotations mit ihrer trivialen Umsetzung.

Für die Unterschiede:

Auffallend ist, dass

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

haben alle Laufzeit-Anmerkungen ( @Retention(RUNTIME) ), während

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

sind nur zur Kompilierzeit ( @Retention(CLASS) ).

Wie beschrieben in diese SO-Antwort die Auswirkungen von Laufzeitkommentaren ist geringer als man denken könnte, aber sie haben den Vorteil dass sie Werkzeuge in die Lage versetzen, zusätzlich zu den Prüfungen zur Kompilierzeit durchzuführen.

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

Paket

FELD

METHODE

PARAMETER

LOKALE_VARIABLE

Android.support.annotation

edu.umd.cs.findbugs.annotations

org.jetbrains.annotation

Lombok

javax.validation.constraints

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

Dies führt zu folgenden Ergebnissen

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

in dieser Runde.

Code

Um Ihnen zu helfen, weitere Details selbst zu vergleichen, führe ich unten den Code jeder Anmerkung auf. Um den Vergleich zu erleichtern, habe ich Kommentare, Importe und die @Documented Bemerkung. (Sie alle hatten @Documented mit Ausnahme der Klassen aus dem Android-Paket). Ich habe die Zeilen neu sortiert und @Target Felder und normalisierte die Qualifikationen.

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 sind 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 beiden Pakete haben keine @Nullable deshalb führe ich sie getrennt auf; Lombok hat eine ziemlich langweilige @NonNull . Unter javax.validation.constraints die @NonNul ist eigentlich ein @NotNull und die Umsetzung ist langwierig.

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

Aus meiner Erfahrung, javax.annotation wird zumindest 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, wird die javax.annotation ( JSR-305 ) ist im Moment noch die beste Lösung.

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


Quellen

  • 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
  • lombok de lombok übergeben. f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints de validation-api-1.0.0.GA-sources.jar

100voto

Bert F Punkte 81431

Ich mag sehr gerne die Checker Framework die eine Implementierung von Typ-Anmerkungen ist ( JSR-308 ), die zur Implementierung von Fehlerprüfgeräten wie einem Nullity Checker verwendet wird. Ich habe keine anderen Programme ausprobiert, um einen Vergleich anzustellen, aber ich bin 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 eine Fehlerprüfung für Nichtigkeit (@Nullable), hat aber auch solche für Unveränderlichkeit y Praktikum (und andere). Ich verwende die erste (Nichtigkeit) und versuche, mich mit der zweiten (Unveränderlichkeit/IGJ) zu befassen. Die dritte probiere ich gerade aus, bin mir aber noch nicht sicher, ob ich sie langfristig nutzen kann. Ich bin von der allgemeinen Nützlichkeit der anderen Prüfer noch nicht überzeugt, aber es ist schön zu wissen, dass das Framework selbst ein System zur Implementierung einer Vielzahl von zusätzlichen Anmerkungen und Prüfern ist.

  2. En Standardeinstellung für die Nichtigkeitsprüfung funktioniert gut: Non-null except locals (NNEL). Im Grunde bedeutet dies, dass der Checker standardmäßig alles (Instanzvariablen, Methodenparameter, generische Typen usw.) außer lokalen Variablen so behandelt, als hätten sie einen @NonNull-Typ. Gemäß der Dokumentation:

    Der NNEL-Standard führt zu der geringsten Anzahl expliziter Anmerkungen in Ihrem Code.

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

  3. Dieses Framework ermöglicht Ihnen die Verwendung mit ohne eine Abhängigkeit von dem Rahmenwerk zu schaffen indem Sie Ihre Anmerkungen in einen Kommentar einschließen: z. B. /*@Nullable*/ . Das ist praktisch, weil man eine Bibliothek oder einen gemeinsam genutzten Code mit Anmerkungen versehen und prüfen kann, aber dennoch in der Lage ist, diese Bibliothek/den gemeinsam genutzten Code in einem anderen Projekt zu verwenden, das das Framework nicht nutzt. Das ist eine schöne Funktion. Ich habe mich daran gewöhnt, sie zu benutzen, obwohl ich dazu neige, das Checker-Framework jetzt für alle meine Projekte zu aktivieren.

  4. Der Rahmen bietet eine Möglichkeit, um APIs annotieren die Sie verwenden und die nicht bereits durch die Verwendung von Stub-Dateien als nichtig gekennzeichnet sind.

63voto

Sam Barnum Punkte 10220

Ich verwende die von IntelliJ, weil ich vor allem darauf achte, dass IntelliJ Dinge anzeigt, die zu einer NPE führen könnten. Ich stimme zu, dass es frustrierend ist, keine Standard-Anmerkung im JDK zu haben. Es wird darüber gesprochen, sie hinzuzufügen, vielleicht schafft sie es in Java 7. In diesem Fall wird es eine weitere Annotation geben, aus der man wählen kann!

33voto

Stephen C Punkte 665668

Nach Angaben der Liste der Java 7-Funktionen JSR-308-Typ-Annotationen werden auf Java 8 verschoben. JSR-305-Annotationen werden nicht einmal erwähnt.

Es gibt ein paar Informationen über den Stand von JSR-305 in einem anhang des letzten JSR-308-Entwurfs. Dazu gehört auch die Feststellung, dass die JSR-305-Anmerkungen anscheinend aufgegeben werden. Auf der JSR-305-Seite wird sie auch als "inaktiv" angezeigt.

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


Tatsächlich definiert JSR-308 keine Annotationstypen/-klassen, und es sieht so aus, als ob sie denken, dass es außerhalb des Anwendungsbereichs liegt. (Und sie haben Recht, angesichts der Existenz von JSR-305).

Wenn JSR-308 jedoch wirklich den Weg in Java 8 findet, würde es mich nicht überraschen, wenn das Interesse an JSR-305 wieder aufleben würde. Soweit ich weiß, hat das JSR-305-Team seine Arbeit nicht formell aufgegeben. Es ist nur seit über 2 Jahren still geworden.

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

28voto

James Wald Punkte 13436

Für Android-Projekte sollten Sie android.support.annotation.NonNull y android.support.annotation.Nullable . Diese und andere hilfreiche Android-spezifische Anmerkungen finden Sie in der Bibliothek unterstützen .

En http://tools.Android.com/tech-docs/support-annotations :

Die Unterstützungsbibliothek selbst wurde ebenfalls mit diesen Angaben versehen Annotationen versehen, so dass Android Studio als Benutzer der Unterstützungsbibliothek Android Studio bereits Ihren Code prüfen und mögliche Probleme auf der Grundlage dieser Annotationen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X