[java] 어떤 @NotNull Java 주석을 사용해야합니까?

NullPointerExceptions을 피하기 위해 IDE 코드 검사 및 정적 코드 분석 (FindBugs 및 Sonar)과 같은 툴링을 사용하고 코드를 더 읽기 쉽게 만들고 싶습니다. 많은 도구가 서로의 @NotNull/ @NonNull/ @Nonnull주석과 호환되지 않는 것처럼 보이며 내 코드에 모든 도구를 나열하는 것은 끔찍할 것입니다. 어느 것이 ‘최고’인지에 대한 제안? 내가 찾은 동등한 주석 목록은 다음과 같습니다.

  • javax.validation.constraints.NotNull
    정적 분석이 아닌 런타임 유효성 검사를 위해 작성되었습니다.
    선적 서류 비치

  • edu.umd.cs.findbugs.annotations.NonNull
    에서 사용 Findbugs 때문에 정적 분석 및 수중 음파 탐지기 (지금 Sonarqube )
    문서

  • javax.annotation.Nonnull
    이것은 Findbugs에서도 작동하지만 JSR-305 는 비활성화되어 있습니다. ( JSR 305의 상태는 무엇입니까? 참조 )
    source

  • org.jetbrains.annotations.NotNull
    정적 분석을 위해 IntelliJ IDEA IDE에서 사용합니다.
    선적 서류 비치

  • lombok.NonNull
    Project Lombok 에서 코드 생성을 제어하는 ​​데 사용됩니다 .
    표준이 없으므로 자리 표시 자 주석
    소스 ,
    문서

  • android.support.annotation.NonNull
    지원 주석 패키지 설명서를 통해 제공되는 Android에서 마커 주석 사용 가능

  • org.eclipse.jdt.annotation.NonNull
    정적 코드 분석 문서를 위해 Eclipse에서 사용



답변

이후 JSR 305 (누구의 목표를 표준화하는 것이었다 @NonNull@Nullable) 몇 년 동안 잠복하고있다, 난 두려워 좋은 답변이 없습니다. 우리가 할 수있는 것은 실용적인 해결책을 찾는 것입니다.

통사론

순수한 스타일 관점에서 IDE, 프레임 워크 또는 Java 자체를 제외한 툴킷에 대한 언급을 피하고 싶습니다.

이것은 배제한다 :

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

어느 하나 우리 잎 javax.validation.constraints이나 javax.annotation. 전자는 JEE와 함께 제공됩니다. 이것이 javax.annotationJSE와 함께 제공되거나 전혀 제공되지 않는 것보다 낫다면 논쟁의 여지가 있습니다. javax.annotationJEE 의존성이 마음에 들지 않기 때문에 개인적으로 선호합니다 .

이것은 우리를 떠나

javax.annotation

또한 가장 짧은 것입니다.

더 좋은 구문은 하나뿐입니다 java.annotation.Nullable. 다른 패키지를 졸업대로 javaxjava과거의 javax.annotation의 오른쪽 방향으로 단계가 될 것입니다.

이행

나는 그들이 기본적으로 모두 동일한 사소한 구현을 가지기를 바랐지만 자세한 분석은 이것이 사실이 아니라는 것을 보여주었습니다.

유사점 우선 :

@NonNull주석 모든 라인을 가지고

public @interface NonNull {}

제외하고

  • org.jetbrains.annotations그것을 호출 @NotNull하고 사소한 구현이 있습니다.
  • javax.annotation 더 긴 구현이 있습니다
  • javax.validation.constraints또한 그것을 호출 @NotNull하고 구현이 있습니다.

@Nullable주석 모든 라인을 가지고

public @interface Nullable {}

org.jetbrains.annotations사소한 구현으로 (다시)를 제외하고 .

차이점을 위해 :

눈에 띄는 것은

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

모두 런타임 주석 ( @Retention(RUNTIME))이 있지만

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

컴파일 시간 ( @Retention(CLASS))입니다.

이 SO 답변에 설명 된 것처럼 런타임 주석의 영향은 생각보다 작지만 컴파일 시간 외에도 도구를 사용하여 런타임 검사를 수행 할 수 있다는 이점이 있습니다.

또 다른 중요한 차이점은 어디에 코드에서 주석을 사용할 수 있습니다. 두 가지 접근 방식이 있습니다. 일부 패키지는 JLS 9.6.4.1 스타일 컨텍스트를 사용합니다. 다음 표는 개요를 제공합니다.

                                필드 방법 매개 변수 LOCAL_VARIABLE
android.support.annotation XXX
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation XXXX
롬복 XXXX
javax.validation.constraints XXX

org.eclipse.jdt.annotation, javax.annotation그리고 org.checkerframework.checker.nullness.qual그것을 할 올바른 방법은 내 의견에 JLS 4.11에 정의 된 컨텍스트를 사용합니다.

이것은 우리를 떠나

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

이 라운드에서.

암호

자세한 내용을 직접 비교할 수 있도록 아래에 모든 주석의 코드를 나열합니다. 비교를 쉽게하기 위해 주석, 가져 오기 및 @Documented주석을 제거했습니다 . (모두 @DocumentedAndroid 패키지의 클래스를 제외하고) 나는 라인과 @Target필드를 재정렬하고 자격을 정규화했습니다.

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

완성을 위해 @Nullable구현은 다음과 같습니다.

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

다음 두 패키지에는가 없으므로 @Nullable별도로 나열합니다. 롬복은 꽤 지루 @NonNull합니다. 에서 javax.validation.constraints(가) @NonNull실제로이며 @NotNull
그것은 기름 한 구현이있다.

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

지원하다

내 경험상, javax.annotation적어도 Eclipse와 Checker Framework는 기본적으로 지원됩니다.

요약

이상적인 주석은 java.annotationChecker Framework 구현 의 구문입니다.

Checker Framework를 사용하지 않으 려면 당분간 javax.annotation( JSR-305 )이 가장 좋습니다.

Checker Framework에 기꺼이 구매하려는 경우을 사용하십시오 org.checkerframework.checker.nullness.qual.


출처

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

답변

필자 는 nullness checker와 같은 결함 검사기를 구현하는 데 사용되는 형식 주석 ( JSR-308 ) 의 구현 인 Checker Framework를 매우 좋아합니다 . 나는 비교를 제공하기 위해 다른 것을 시도하지는 않았지만이 구현에 만족했습니다.

저는 소프트웨어를 제공하는 그룹과 제휴하지는 않지만 팬입니다.

이 시스템에 대해 내가 좋아하는 네 가지 :

  1. Null에 대한 결함 검사기 (@Nullable)가 있지만 불변성인턴 (및 기타)에 대한 결함 검사기 도 있습니다 . 나는 첫 번째 (nullness)를 사용하고 두 번째 것을 사용하려고합니다 (불변성 / IGJ). 세 번째 방법을 시도하고 있지만 아직 장기적으로 사용하고 있는지 확실하지 않습니다. 나는 다른 체커의 일반적인 유용성을 아직 확신하지 못하지만 프레임 워크 자체가 다양한 추가 주석 및 체커를 구현하기위한 시스템이라는 것을 알고 있습니다.

  2. 그만큼 널 (Null) 검사 기본 설정은 다음과 같이 작동합니다. NEL (Null을 제외한 널 없음). 기본적으로 이는 체커가 기본적으로 @NonNull 유형을 갖는 것처럼 로컬 변수를 제외한 모든 변수 (인스턴스 변수, 메소드 매개 변수, 일반 유형 등)를 처리한다는 것을 의미합니다. 설명서에 따라 :

    NNEL 기본값은 코드에서 가장 적은 수의 명시 적 주석으로 이어집니다.

    NNEL이 작동하지 않으면 클래스 또는 메소드에 대해 다른 기본값을 설정할 수 있습니다.

  3. 이 프레임 워크를 사용하면 주석에 주석을 묶어 프레임 워크대한 종속성을 만들지 않고 함께 사용할 수 있습니다 ./*@Nullable*/ . 라이브러리 또는 공유 코드에 주석을 달고 확인할 수는 있지만 프레임 워크를 사용하지 않는 다른 프로젝트에서 해당 라이브러리 / 공유 코드를 계속 사용할 수 있기 때문에 좋습니다. 이것은 좋은 기능입니다. 현재 모든 프로젝트에서 Checker Framework를 사용하는 경향이 있지만 사용에 익숙해졌습니다.

  4. 프레임 워크에는 스텁 파일을 사용하여 널 (null)에 대해 아직 주석을 달지 않은 사용하는 API주석달 수 있는 방법이 있습니다.


답변

나는 IntelliJ를 사용하는데, 이는 NPE를 생성 할 수있는 것들을 표시하는 IntelliJ에 주로 관심이 있기 때문입니다. JDK에 표준 주석이없는 것이 실망 스럽습니다. 그것을 추가하는 것에 대한 이야기가 있는데, 그것은 Java 7로 만들 수 있습니다.이 경우 선택할 수있는 것이 하나 더 있습니다!


답변

Java 7 기능 목록 에 따르면 JSR-308 유형 주석은 Java 8로 연기됩니다. JSR-305 주석은 언급되지 않았습니다.

최신 JSR-308 초안 의 부록 에 JSR-305 상태에 대한 약간의 정보가 있습니다. 여기에는 JSR-305 주석이 폐기 된 것으로 보입니다. JSR-305 페이지에도 “비활성”으로 표시됩니다.

그 동안 실용적인 답변은 가장 널리 사용되는 도구가 지원하는 주석 유형을 사용하고 상황이 바뀌면 변경할 수 있도록 준비하는 것입니다.


실제로 JSR-308은 주석 유형 / 클래스를 정의하지 않으며 범위를 벗어난 것으로 생각됩니다. (그리고 JSR-305의 존재를 감안할 때 그들은 옳습니다).

그러나 JSR-308이 실제로 Java 8로 만들어지는 것처럼 보이면 JSR-305에 대한 관심이 다시 살아나더라도 놀라지 않을 것입니다. JSR-305 팀인 AFAIK는 공식적으로 업무를 포기하지 않았습니다. 그들은 2 년 이상 조용했습니다.

Bill Pugh (JSR-305의 기술 책임자)가 FindBugs의이면에 있다는 것은 흥미 롭습니다.


답변

Android 프로젝트의 경우 android.support.annotation.NonNull및 을 사용해야합니다 android.support.annotation.Nullable. 이러한 유용한 기타 Android 관련 주석은 지원 라이브러리 에서 사용할 수 있습니다 .

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

지원 라이브러리 자체에도 이러한 주석으로 주석이 달렸으므로 지원 라이브러리의 사용자로서 Android Studio는 이미 주석을 기반으로 코드를 확인하고 잠재적 인 문제를 표시합니다.


답변

누구나 IntelliJ 클래스를 찾고 있다면 다음을 사용하여 maven 저장소에서 가져올 수 있습니다

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


답변

JSR305와 FindBugs는 같은 사람이 작성합니다. 둘 다 잘 관리되지 않았지만 모든 주요 IDE에서 지원하고 지원하는 표준입니다. 좋은 소식은 그대로 작동한다는 것입니다.

기본적으로 @Nonnull을 모든 클래스, 메서드 및 필드에 적용하는 방법은 다음과 같습니다. 참조 https://stackoverflow.com/a/13319541/14731https://stackoverflow.com/a/9256595/14731

  1. 밝히다 @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. 각 패키지에 주석을 추가하십시오. package-info.java

@NotNullByDefault
package com.example.foo;

업데이트 : 2012 년 12 월 12 일 현재 JSR 305 는 “Dormant”로 표시됩니다. 설명서에 따르면 :

집행위원회에 의해 “휴면”으로 선정 된 JSR 또는 자연 수명이 다한 JSR.

JSR 308 JDK 8로 만드는 것처럼 보이고 JSR이 @NotNull을 정의하지는 않지만 첨부 된 것 Checkers Framework입니다. 이 글을 쓰는 시점 에서이 버그로 인해 Maven 플러그인을 사용할 수 없습니다 : https://github.com/typetools/checker-framework/issues/183