프로그래밍 언어/JAVA

NullPointerExceptions를 방지하기 위해 어떤 @NotNull Java 주석을 사용해야 할까?

Rateye 2021. 12. 29. 11:14
728x90
반응형
질문 : 어떤 @NotNull Java 주석을 사용해야합니까?

NullPointerExceptions를 방지하기 위해 IDE 코드 검사 및 / 또는 정적 코드 분석 (FindBugs 및 Sonar)과 같은 도구를 사용하는 것뿐만 아니라 코드를 더 읽기 쉽게 만들고 싶습니다. 많은 도구가 서로의 @NotNull / @NonNull / @Nonnull 주석과 호환되지 않는 것처럼 보이며 모두 내 코드에 나열하면 읽기가 끔찍합니다. 어떤 것이 '최고'인지에 대한 제안이 있습니까? 내가 찾은 동등한 주석 목록은 다음과 같습니다.

 

답변

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와 함께 제공됩니다. 이것이 결국 JSE와 함께 제공되거나 전혀 제공되지 않는 javax.annotation 보다 낫다면 논쟁의 여지가 있습니다. JEE 종속성이 마음에 들지 않기 때문에 개인적으로 javax.annotation

이것은 우리에게

javax.annotation

이것은 또한 가장 짧은 것입니다.

더 나은 구문은 java.annotation.Nullable 입니다. 과거에 다른 패키지가 javax 에서 java 로 전환되었으므로 javax.annotation은 올바른 방향으로 나아가는 단계가 될 것입니다.

구현

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

먼저 유사점 :

@NonNull 주석에는 모두 다음 줄이 있습니다.

public @interface NonNull {}

제외하고

  • @NotNull 이라고 부르고 간단한 구현이있는 org.jetbrains.annotations
  • 더 긴 구현이있는 javax.annotation
  • @NotNull 이라고도 부르고 구현이있는 javax.validation.constraints

@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 스타일 컨텍스트를 사용합니다. 다음 표는 개요를 제공합니다.

                                FIELD   METHOD  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.annotationorg.checkerframework.checker.nullness.qual 은 JLS 4.11에 정의 된 컨텍스트를 사용하는데, 이는 제 생각에는 올바른 방법입니다.

이것은 우리에게

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

이번 라운드에서.

 

반응형

 

코드

자세한 내용을 직접 비교할 수 있도록 아래에 모든 주석의 코드를 나열합니다. 비교를 더 쉽게하기 위해 주석, 가져 오기 및 @Documented 주석을 제거했습니다. (Android 패키지의 클래스를 제외하고 @Documented 줄과 @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 이 없으므로 별도로 나열합니다. Lombok에는 꽤 지루한 @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에서 즉시 지원됩니다.

 

요약

이상적인 주석은 Checker Framework 구현을 사용한 java.annotation

Checker Framework를 사용하지 않으려는 경우 javax.annotation ( JSR-305 )이 당분간 최선의 방법입니다.

Checker Framework를 구매하려는 경우 org.checkerframework.checker.nullness.qual 사용하십시오.


소스

  • android-5.1.1_r1.jar android.support.annotation
  • edu.umd.cs.findbugs.annotations 에서 findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotationorg.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotations jetbrains-annotations-13.0.jar
  • gwt-dev-2.5.1-sources.jar javax.annotation
  • org.checkerframework.checker.nullness.qualchecker-framework-2.1.9.zip
  • lombok commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4 에서 lombok
  • validation-api-1.0.0.GA-sources.jar javax.validation.constraints
출처 : https://stackoverflow.com/questions/4963300/which-notnull-java-annotation-should-i-use
728x90
반응형