programing

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

nasanasas 2020. 9. 28. 09:13
반응형

어떤 @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의 상태는 무엇입니까? ) 소스

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

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

  • android.support.annotation.NonNull
    Android에서 사용 가능한 마커 주석 (support-annotations 패키지
    문서에서 제공)

  • 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.annotation결국 JSE와 함께 제공되거나 전혀 제공되지 않을 수있는 보다 낫다면 논쟁의 여지가 있습니다. 나는 javax.annotationJEE 의존성을 좋아하지 않기 때문에 개인적으로 선호합니다 .

이것은 우리에게

javax.annotation

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

더 나은 구문은 하나뿐입니다 java.annotation.Nullable. 다른 패키지 가 과거에 에서 javax졸업했듯이 javajavax.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 스타일 컨텍스트를 사용합니다. 다음 표는 개요를 제공합니다.


                                FIELD METHOD PARAMETER 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. nullness (@Nullable)에 대한 결함 검사기가 있지만 불변성인턴 (및 기타)에 대한 검사기 도 있습니다 . 나는 첫 번째 (nullness)를 사용하고 두 번째 (불변성 / IGJ)를 사용하려고합니다. 세 번째를 시도하고 있지만 아직 장기간 사용하는 것이 확실하지 않습니다. 아직 다른 검사기의 일반적인 유용성은 확신하지 못하지만 프레임 워크 자체가 다양한 추가 주석 및 검사기를 구현하는 시스템이라는 사실을 알게되어 기쁩니다.

  2. nullness 검사기본 설정 은 잘 작동합니다. 로컬을 제외하고 null이 아닙니다 (NNEL). 기본적으로 이것은 기본적으로 검사기가 로컬 변수를 제외한 모든 항목 (인스턴스 변수, 메서드 매개 변수, 제네릭 유형 등)을 기본적으로 @NonNull 유형을 갖는 것처럼 처리한다는 것을 의미합니다. 문서에 따라 :

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

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

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

  4. 프레임 워크에는 스텁 파일을 사용하여 아직 nullness 주석이 추가되지 않은 API주석추가 하는 방법이 있습니다.


IntelliJ를 사용합니다. 주로 IntelliJ가 NPE를 생성 할 수있는 항목을 표시하는 데 관심이 있기 때문입니다. 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에 대한 관심이 다시 살아나더라도 놀라지 않을 것입니다. AFAIK, JSR-305 팀은 공식적으로 작업을 포기하지 않았습니다. 그들은 2 년 이상 조용했습니다.

Bill Pugh (JSR-305의 기술 책임자)가 FindBugs의 배후에있는 사람 중 한 명이라는 것은 흥미 롭습니다.


Android 프로젝트의 경우 android.support.annotation.NonNullandroid.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 는 "휴면"으로 표시됩니다. 문서에 따르면 :

집행위원회에서 "휴면"으로 투표 한 JSR 또는 자연 수명이 다한 JSR입니다.

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


Eclipse에는 자체 주석도 있습니다.

org.eclipse.jdt.annotation.NonNull

자세한 내용은 http://wiki.eclipse.org/JDT_Core/Null_Analysis 를 참조하십시오.


Java Validation API ( javax.validation.constraints.*)에는 @Nullable주석 이 포함되어 있지 않으므로 정적 분석 컨텍스트에서 매우 유용합니다. 이것은 Java의 기본이 아닌 필드에 대한 기본값이므로 런타임 빈 유효성 검사에 적합합니다 (즉, 유효성 검사 / 시행 할 항목 없음). 목적을 위해 대안에 무게를 두어야합니다.


정적 분석과 런타임 분석을 구분합니다. 내부 항목에는 정적 분석을 사용하고 코드의 공개 경계에는 런타임 분석을 사용하십시오.

null이 아니어야하는 항목의 경우 :

  • 런타임 검사 : "if (x == null) ..."(종속성 없음) 또는 @ javax.validation.NotNull (빈 유효성 검사 포함) 또는 @ lombok.NonNull (일반 및 단순) 또는 guavas Preconditions.checkNotNull (.. .)

    • 메서드 반환 유형에 대해 Optional을 사용합니다 (전용). Java8 또는 Guava 중 하나입니다.
  • 정적 검사 : @NonNull 주석 사용

  • 적합한 경우 클래스 또는 패키지 수준에서 @ ... NonnullByDefault 주석을 사용하십시오. 이러한 주석을 직접 만듭니다 (예는 찾기 쉽습니다).
    • 그렇지 않으면 NPE를 피하기 위해 메서드 반환에 @ ... CheckForNull을 사용합니다.

이것은 최상의 결과를 제공해야합니다 : IDE의 경고, Findbugs 및 checkerframework의 오류, 의미있는 런타임 예외.

정적 검사가 성숙 될 것이라고 기대하지 마십시오. 이름이 표준화되지 않았으며 다른 라이브러리와 IDE가 다르게 처리하고 무시합니다. JSR305 javax.annotations. * 클래스는 표준처럼 보이지만 그렇지 않으며 Java9 +로 분할 패키지를 발생시킵니다.

일부 메모 설명 :

  • javax.validation. * 패키지가있는 Findbugs / spotbugs / jsr305 주석이 Java9 +의 다른 모듈과 충돌하며 Oracle 라이센스를 위반할 수도 있습니다.
  • Spotbugs 주석은 여전히 ​​컴파일 타임에 jsr305 / findbugs 주석에 의존합니다 ( https://github.com/spotbugs/spotbugs/issues/421 작성 당시 )
  • jetbrains @NotNull 이름이 @ javax.validation.NotNull과 충돌합니다.
  • 정적 검사를위한 jetbrains, eclipse 또는 checkersframework 주석은 Java9 이상에서 다른 모듈과 충돌하지 않는다는 점에서 javax.annotations에 비해 이점이 있습니다.
  • @ javax.annotations.Nullable은 Findbugs / Spotbugs 사용자 (또는 IDE)가 의미하는 바를 의미하지 않습니다. Findbugs는이를 무시합니다 (멤버의 경우). 슬프지만 사실입니다 ( https://sourceforge.net/p/findbugs/bugs/1181 )
  • IDE 외부의 정적 검사를 위해 Spotbugs (이전 Findbugs) 및 checkersframework의 두 가지 무료 도구가 있습니다.
  • Eclipse 라이브러리에는 @NonNullByDefault가 있고 jsr305에는 @ParametersAreNonnullByDefault 만 있습니다. 그것들은 패키지 (또는 클래스)의 모든 것에 기본 주석을 적용하는 단순한 편의 래퍼이며, 쉽게 직접 만들 수 있습니다. 패키지에 사용할 수 있습니다. 생성 된 코드 (예 : lombok)와 충돌 할 수 있습니다.
  • Eclipse jdt 어노테이션은 정적 메소드 리턴 및 기타 일부 경우에 적용되지 않습니다.
  • lombok을 내 보낸 종속성으로 사용하는 것은 다른 사람들과 공유하는 라이브러리에 대해 피해야합니다. 전 이적 종속성이 적을수록 좋습니다.
  • Bean 유효성 검사 프레임 워크를 사용하는 것은 강력하지만 높은 오버 헤드가 필요하므로 수동 null 검사를 피하기 위해 과잉입니다.
  • 필드 및 메소드 매개 변수에 선택 사항을 사용하는 것은 논란의 여지가 있습니다 (이에 대한 기사를 쉽게 찾을 수 있음).
  • Android null 주석은 Android 지원 라이브러리의 일부이며 다른 많은 클래스와 함께 제공되며 다른 주석 / 도구와 잘 작동하지 않습니다.

Java9 이전에는 이것이 내 권장 사항입니다.

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

nullable 메서드 매개 변수가 역 참조 될 때 Spotbugs가 경고를 발생 시키도록 할 수있는 방법이 없습니다 (작성 당시 Spotbugs 버전 3.1). 아마도 checkerframework가 그렇게 할 수 있습니다.

슬프게도 이러한 주석은 임의의 호출 사이트가있는 라이브러리의 공용 메서드와 각 호출 사이트를 알 수있는 비공개 메서드의 경우를 구분하지 않습니다. 따라서 "null이 원치 않는다는 것을 나타내지 만 그럼에도 불구하고 null이 전달 될 준비를한다"라는 이중 의미는 단일 선언에서 불가능합니다. 따라서 위의 예제는 인터페이스와 구현에 대해 다른 주석을 가지고 있습니다.

분할 인터페이스 접근 방식이 실용적이지 않은 경우 다음 접근 방식이 절충안입니다.

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

이를 통해 클라이언트는 null (올바른 코드 작성)을 전달하지 않고 유용한 오류를 반환 할 수 있습니다.


안타깝게도이 JSR 308프로젝트 로컬 Not Null 제안보다 더 많은 값을 추가하지 않습니다.

Java 8단일 기본 주석 또는 자체 Checker프레임 워크 가 제공되지 않습니다 . Find-bugs 또는와 유사하게 JSR 305,이 JSR은 대부분의 학업 팀에 의해 제대로 관리되지 않습니다.

그 배후에 상업적 전력이 없기 때문에 지금 (Early Draft Review at ) JSR 308시작 하지만 6 개월 이내에 배송 될 예정입니다 .-O btw 와 유사합니다 . 하지만 지금은 자바 플랫폼에 미칠 피해를 최소화하기 위해 창립자로부터 책임을 맡았습니다.EDR 3JCPJava 8310308 Oracle

뒤에서 것과 같은 모든 프로젝트, 공급 업체 및 학술 클래스 Checker Framework와는 JSR 308독자적인 검사 주석을 생성합니다.

몇 가지 인기있는 타협이 발견되고 Java 9또는에 추가 될 수있을 때까지 또는 또는 ;-) 10와 같은 프레임 워크를 통해 소스 코드를 향후 몇 년 동안 호환되지 않게 만들기Apache CommonsGoogle Guava


기계적 인조 인간

이 답변은 Android 전용입니다. Android에는 support-annotations. 이 제공하는 수십 가지안드로이드 특정의 주석을 또한 제공합니다 일반적인 사람 처럼 NonNull, Nullable

추가 지원 - 주석 패키지를, 당신의 build.gradle에 다음 종속성을 추가 :

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

다음을 사용하십시오.

import android.support.annotation.NonNull;

void foobar(@NonNull Foo bar) {}

이것이 업스트림에서 정렬되기를 기다리는 동안 (Java 8?) 자신 만의 프로젝트 로컬 @NotNull@Nullable주석을 정의 할 수도 있습니다 . 이 기능은 기본적으로 javax.validation.constraints 사용할 수없는 Java SE로 작업하는 경우에도 유용 할 수 있습니다 .

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

위의 내용 자체가 이러한 주석의 정적 분석에 대한 지원을 추가하지 않기 때문에 이는 대부분 장식 또는 미래 보장 목적을위한 것입니다.


Android 용으로 개발하는 경우 자체 주석이있는 Eclipse (편집 : 더 이상 작성하지 않음)에 다소 묶여 있습니다. Eclipse 3.8+ (Juno)에 포함되어 있지만 기본적으로 비활성화되어 있습니다.

Preferences> Java> Compiler> Errors / Warnings> Null analysis (하단의 축소 가능한 섹션)에서 활성화 할 수 있습니다.

"주석 기반 널 분석 사용"을 선택하십시오.

http://wiki.eclipse.org/JDT_Core/Null_Analysis#Usage 에는 설정에 대한 권장 사항이 있습니다. 그러나 작업 영역에 외부 프로젝트 (예 : facebook SDK)가있는 경우 이러한 권장 사항을 충족하지 못할 수 있으며 각 SDK 업데이트로 수정하고 싶지 않을 것입니다 ;-)

나는 사용한다:

  1. Null 포인터 액세스 : 오류
  2. 널 스펙 위반 : 오류 (점 # 1에 링크 됨)
  3. 잠재적 널 포인터 액세스 : 경고 (그렇지 않으면 facebook SDK에 경고가 있음)
  4. null 주석과 null 추론 간의 충돌 : 경고 (점 # 3에 연결됨)

큰 프로젝트에서 작업하는 경우 자체 @Nullable 및 / 또는 @NotNull주석 을 만드는 것이 더 나을 수 있습니다 .

예를 들면 :

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

올바른 보존 정책 을 사용하면 런타임에 주석 을 사용할 수 없습니다 . 그 관점에서 그것은 단지 내부적 인 것입니다.

이것은 엄격한 과학은 아니지만 내부 수업 을 사용하는 것이 가장 합리적이라고 생각 합니다.

  • 내부적 인 것입니다. (기능적 또는 기술적 영향 없음)
  • 많은 많은 용도로.
  • IntelliJ와 같은 IDE는 사용자 정의 @Nullable/ @NotNull주석을 지원 합니다.
  • 대부분의 프레임 워크는 자체 내부 버전도 선호합니다.

추가 질문 (댓글 참조) :

IntelliJ에서 이것을 구성하는 방법은 무엇입니까?

IntelliJ 상태 표시 줄의 오른쪽 하단에있는 "경찰관"을 클릭합니다. 그리고 팝업에서 "검사 구성"을 클릭합니다. 다음 ...주석 구성


Java 8에서이 작업을 수행하는 또 다른 방법이 있습니다. 필요한 작업을 수행하기 위해 두 가지 작업을 수행하고 있습니다.

  1. nullable 필드를 다음으로 래핑하여 형식으로 nullable 필드를 명시 적으로 만들기 java.util.Optional
  2. nullable이 아닌 모든 필드가 생성시 null이 아닌지 확인합니다. java.util.Objects.requireNonNull

예:

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

그래서 제 질문은 Java 8을 사용할 때 주석을 달아야합니까?

편집 : 나중에 일부 사람들이 Optional인수 에 사용하는 나쁜 관행을 고려한다는 것을 알게되었으며 여기에 장단점에 대한 좋은 토론이 있습니다. 왜 Java 8의 옵션을 인수에 사용하지 않아야합니까?


여기에는 이미 너무 많은 답변이 있지만 (a) 2019 년이고 여전히 "표준"이 Nullable없으며 (b) Kotlin을 참조하는 다른 답변이 없습니다.

Kotlin은 Java와 100 % 상호 운용 가능하고 핵심 Null Safety 기능이 있기 때문에 Kotlin에 대한 참조가 중요합니다. 자바 라이브러리를 호출 할 때 이러한 주석을 활용하여 Kotlin 도구에 자바 API가 null.

내가 아는 Nullable한 Kotlin org.jetbrains.annotations호환되는 유일한 패키지는 android.support.annotation(현재 androidx.annotation)입니다. 후자는 Android 와만 호환되므로 Android 이외의 JVM / Java / Kotlin 프로젝트에서는 사용할 수 없습니다. 그러나 JetBrains 패키지는 모든 곳에서 작동합니다.

따라서 Android 및 Kotlin에서도 작동하고 Android Studio 및 IntelliJ에서 지원해야하는 Java 패키지를 개발하는 경우 가장 좋은 선택은 아마도 JetBrains 패키지 일 것입니다.

메이븐 :

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

Gradle :

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

태양은 지금 자신의 것을 가지고 있지 않습니까? 내용 :
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Modules-com.sun/istack/com.sun.istack.internal.htm

이것은 지난 몇 년 동안 내가 사용한 모든 버전의 Java와 함께 패키지 된 것 같습니다.

편집 : 아래 주석에서 언급했듯이 아마도 이것을 사용하고 싶지 않을 것입니다. 이 경우 내 투표는 IntelliJ jetbrains 주석입니다!


당신은 스프링 프레임 워크 I을 사용하여 응용 프로그램을 구축하는 경우 사용하는 제안 javax.validation.constraints.NotNull에서 오는 콩 검증 다음 의존성에 포장 :

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

이 어노테이션의 주요 장점은 Spring이로 어노테이션이있는 클래스 필드와 메소드 매개 변수를 모두 지원한다는 것입니다 javax.validation.constraints.NotNull. 지원을 활성화하기 위해해야 ​​할 일은 다음과 같습니다.

  1. 빈 유효성 검사를위한 api jar와 jsr-303 / jsr-349 주석의 유효성 검사기를 구현 한 jar를 제공합니다 (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. Spring의 컨텍스트에 MethodValidationPostProcessor 제공

      @Configuration
      @ValidationConfig
      public class ValidationConfig implements MyService {
    
            @Bean
            public MethodValidationPostProcessor providePostProcessor() {
                  return new MethodValidationPostProcessor()
            }
      }
    
  3. 마지막으로 클래스에 Spring의 주석을 달고 org.springframework.validation.annotation.Validated유효성 검사는 Spring에 의해 자동으로 처리됩니다.

예:

@Service
@Validated
public class MyServiceImpl implements MyService {

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

doSomething 메서드를 호출하고 매개 변수 값으로 null을 전달하려고하면 (HibernateValidator를 통해) spring이 throw ConstraintViolationException됩니다. 여기서는 수동 작업이 필요 없습니다.

반환 값의 유효성을 검사 할 수도 있습니다.

javax.validation.constraints.NotNullBeans Validation Framework 의 또 다른 중요한 이점 은 현재 아직 개발 중이며 새 버전 2.0에 대한 새로운 기능이 계획되어 있다는 것입니다.

어때 @Nullable? Beans Validation 1.1에는 이와 같은 것이 없습니다. 글쎄요, 당신이 사용하기로 결정했다면 @NotNull주석 이 붙지 않은 모든 @NonNull것이 사실상 "nullable"이기 때문에 @Nullable주석은 쓸모가 없다고 주장 할 수 있습니다.


또 다른 옵션은 ANTLR 4와 함께 제공되는 주석입니다. Pull Request # 434 에 따라 @NotNull@Nullable주석을 포함하는 아티팩트 에는 이러한 속성 중 하나가 오용되는 경우 컴파일 시간 오류 및 / 또는 경고를 생성하는 주석 프로세서가 포함됩니다 (예 : 둘 다 동일한 항목에 @Nullable적용 되거나 기본 유형이있는 항목에 적용되는 경우 ). 주석 프로세서는 소프트웨어 개발 프로세스 동안 메서드 상속의 경우를 포함하여 이러한 주석을 적용하여 전달되는 정보가 정확하다는 추가 보증을 제공합니다.


IntelliJ의 좋은 점 중 하나는 주석을 사용할 필요가 없다는 것입니다. 직접 작성하거나 원하는 다른 도구를 사용할 수 있습니다. 당신은 단일 유형에 국한되지 않습니다. 서로 다른 @NotNull 주석을 사용하는 두 개의 라이브러리를 사용하는 경우 IntelliJ에 둘 다 사용하도록 지시 할 수 있습니다. 이렇게하려면 "검사 구성"으로 이동하여 "상수 조건 및 예외"검사를 클릭 한 다음 "검사 구성"버튼을 누르십시오. 가능한 모든 곳에서 Nullness Checker를 사용하므로 해당 주석을 사용하도록 IntelliJ를 설정했지만 원하는 다른 도구와 함께 사용할 수 있습니다. (저는 IntelliJ의 검사를 수년 동안 사용해 왔기 때문에 다른 도구에 대한 의견이 없으며 좋아합니다.)

참고 URL : https://stackoverflow.com/questions/4963300/which-notnull-java-annotation-should-i-use

반응형