@Nullable 및 @Nonnull 주석을보다 효과적으로 사용하는 방법은 무엇입니까?


140

나는 그것을 볼 수 있습니다 @Nullable@Nonnull주석 예방에 도움이 될 NullPointerException들하지만 그들은 아주 멀리 전파되지 않습니다.

  • 이러한 주석의 효과는 한 수준의 간접적 인 후에 완전히 사라 지므로 몇 가지만 추가하면 아주 많이 전파되지 않습니다.
  • 이러한 주석이 제대로 적용되지 않기 때문에로 표시된 값 @Nonnull이 널이 아니라고 가정 하여 널 검사를 수행하지 않을 위험이 있습니다.

아래 코드는 불만을 제기하지 않은 것으로 표시된 매개 변수가 발생 @Nonnull하도록합니다 null. NullPointerException실행 되면를 던집니다 .

public class Clazz {
    public static void main(String[] args){
        Clazz clazz = new Clazz();

        // this line raises a complaint with the IDE (IntelliJ 11)
        clazz.directPathToA(null);

        // this line does not
        clazz.indirectPathToA(null); 
    }

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }
}

이러한 주석을보다 엄격하게 적용 및 / 또는 더 전파 할 수있는 방법이 있습니까?


1
I의 생각처럼 @Nullable또는 @Nonnull그들이 가치가있는 경우, 그러나 "SOLICIT 논쟁 가능성이"매우이다
마틴 Bodewes

컴파일러 오류 또는 경고가 발생하는 세계로 이동 @Nonnull하는 @Nonnull방법은 nullable 변수가 있는 메서드를 호출 할 때 캐스트가 필요하다고 생각합니다 . 물론 Java 7에서는 주석을 사용한 캐스트가 불가능하지만 Java 8은 캐스트를 포함하여 변수 사용에 주석을 적용하는 기능을 추가 할 것입니다. 따라서 이것은 Java 8에서 구현 될 수 있습니다.
Theodore Murdock

1
@TheodoreMurdock, 예, Java 8에서는 캐스트 (@NonNull Integer) y가 구문 적으로 가능하지만 컴파일러는 주석을 기반으로 특정 바이트 코드를 생성 할 수 없습니다. 런타임 어설 션의 경우 bugs.eclipse.org/442103 (예 :) 에서 논의 된 것처럼 작은 도우미 메소드이면 충분 directPathToA(assertNonNull(y))하지만, 이는 빠르게 실패하는 데 도움이됩니다. 유일한 안전한 방법은 실제 null 검사를 수행하는 것입니다.
Stephan Herrmann

1
그것은 어떤 말을이 질문에 도움이 될 것 @Nonnull@Nullable유사한 여러 annoations 있기 때문에 (참조, 당신에 대해 얘기하는 이 질문에 ). 패키지의 주석에 대해 이야기하고 javax.annotation있습니까?
James Dunn

1
@TJamesBoone이 질문의 맥락에서 중요하지 않습니다. 이것은 효과적으로 그것들을 사용하는 방법에 관한 것입니다.
Mike Rylander

답변:


66

짧은 대답 :이 주석은 IDE에서 잠재적으로 null 포인터 오류를 경고하는 데 유용합니다.

"Clean Code"책에서 언급했듯이 공용 메소드의 매개 변수를 확인하고 불변 값을 확인하지 않아야합니다.

또 다른 좋은 팁은 null 값을 반환하지 않지만 대신 Null Object Pattern을 사용하는 것입니다.


10
반환 값이 비어있는 경우 Optional일반 대신 유형을 사용하는 것이 좋습니다.null
Patrick

7
선택 사항은 "null"보다 낫지 않습니다. Optional # get ()은 NoSuchElementException을 발생시키고 null을 사용하면 NullPointerException이 발생합니다. 둘 다 RuntimeException이며 의미있는 설명이 없습니다. nullable 변수를 선호합니다.
30

4
@ 30thh 왜 Optional.isPresent () 또는 Optional.map이 아닌 Optional.get ()을 직접 사용 하시겠습니까?
GauravJ

7
@GauravJ nullable 변수를 직접 사용하고 왜 null인지 확인하지 않는 이유는 무엇입니까? ;-)
30

5
Optional이 경우와 nullable 의 차이점은 Optional이 값이 의도적으로 비어있을 수 있음 을 보다 잘 전달한다는 것입니다. 확실히, 그것은 마술 지팡이가 아니며 런타임에서 nullable 변수와 정확히 같은 방식으로 실패 할 수 있습니다. 그러나 프로그래머의 API 수신이 더 좋습니다 Optional.
user1053510

31

null인수가 널이 아닌 것으로 예상되는 메소드에 전달할 때 힌트를 제공하는 IDE 이외의 다른 장점이 있습니다.

  • 정적 코드 분석 도구는 IDE와 동일한 테스트를 수행 할 수 있습니다 (예 : FindBugs)
  • AOP를 사용하여 이러한 어설 션을 확인할 수 있습니다

이렇게하면 코드를 유지 관리 null하기 쉽고 (확인할 필요가 없기 때문에 ) 오류 발생 가능성이 줄어 듭니다.


9
이 두 가지 장점을 인용했지만 두 경우 모두 "can"이라는 단어를 사용했기 때문에 OP에 동조합니다. 즉, 이러한 검사가 실제로 발생한다는 보장이 없습니다. 이 동작 차이는 프로덕션 모드에서 실행을 피하려는 성능에 민감한 테스트에 유용 할 수 있습니다 assert. 나는 발견 @Nullable하고 @Nonnull유용한 아이디어로, 그러나 나는 우리가 한 일에 대해 가정하는 것이 아니라, 그들 뒤에 더 힘을 싶습니다 아직 잎이 그들과 함께 아무것도하지의 가능성을 열 수있는, 그들과 함께 할.
seh

2
문제는 어디서부터 시작해야 하는가입니다. 현재 그의 연설은 선택 사항입니다. Somtimes 어떤 상황에서는 강제로 도움이 될 것이기 때문에 그들이
아니라면 좋겠다

여기서 말하는 AOP가 무엇인지 물어봐도 될까요?
Chris.Zou

@ Chris.Zou AOP는 Aspect 지향 프로그래밍을 의미합니다. 예 : AspectJ
Uwe Plonus

13

이 원래의 질문은 @NonNull을 사용하더라도 런타임 null 포인터 검사가 여전히 필요하다는 일반적인 권장 사항을 간접적으로 가리키는 것으로 생각합니다. 다음 링크를 참조하십시오.

Java 8의 새로운 타입 주석

위의 블로그에서 다음을 권장합니다.

선택적 유형 주석은 런타임 유효성 검증을 대체하지 않습니다. 유형 주석 이전에 널 (null) 가능성 또는 범위와 같은 항목을 설명하는 기본 위치는 javadoc에있었습니다. 형식 주석을 사용하면이 통신은 컴파일 타임 확인을 위해 바이트 코드로 들어옵니다. 코드는 여전히 런타임 유효성 검사를 수행해야합니다.


1
이해했지만 기본 보푸라기 검사는 런타임 null 검사가 필요하지 않다고 경고하며, 처음에는이 권장 사항을 권장하지 않는 것 같습니다.
swooby

1
@swooby 일반적으로 코드가 정확하다면 보푸라기 경고를 무시합니다. 이러한 경고는 오류가 아닙니다.
jonathanzh

12

준수 1.8에서 Eclipse의 원래 예제를 컴파일하고 주석 기반 널 분석을 사용하면 다음 경고가 표시됩니다.

    directPathToA(y);
                  ^
Null type safety (type annotations): The expression of type 'Integer' needs unchecked conversion to conform to '@NonNull Integer'

이 경고는 원시 유형을 사용하여 생성 된 코드와 레거시 코드를 혼합 할 때 발생하는 경고 ( "확인되지 않은 변환")와 유사합니다. 우리는 여기에 똑같은 상황이 있습니다 : 방법indirectPathToA() 에는 null 계약을 지정하지 않는다는 점에서 "legacy"서명이 있습니다. 툴은이를 쉽게보고 할 수 있으므로 널 어노테이션이 전파되어야하지만 아직 그렇지 않은 모든 골목을 추적합니다.

그리고 영리한 것을 사용할 때 @NonNullByDefault 것을 마다 우리는 이것을 말할 필요조차 없습니다.

다시 말해, 널 어노테이션이 "매우 멀리 전파"되는지 여부는 사용하는 도구 및 도구가 발행 한 모든 경고에 얼마나주의를 기울여야하는지에 달려 있습니다. TYPE_USE 널 어노테이션 을 사용하면 널 (NULL) 이 유형 시스템의 중요한 특성이 되었기 때문에 도구가 프로그램에서 가능한 모든 NPE에 대해 경고 할 수있는 옵션이 있습니다.


8

나는 주석이 "아주 멀리 전파되지 않음"에 동의합니다. 그러나 나는 프로그래머 측에서 실수를 본다.

Nonnull주석을 문서로 이해합니다 . 다음 메소드는 널이 아닌 인수가 필요한 (전제 조건으로) 표현합니다 x.

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }

다음 코드 스 니펫에는 버그가 포함되어 있습니다. 메소드는 널이 아닌 directPathToA()것을 강제하지 않고 호출합니다 y(즉, 호출 된 메소드의 전제 조건을 보장하지는 않습니다). 한 가지 가능성은 Nonnull주석 을 추가하는 것 indirectPathToA()(전제 조건 전파)입니다. 가능성 2는 yin의 null을 확인하고 null indirectPathToA()directPathToA()되었을 때 의 호출을 피하는 것 y입니다.

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }

1
(가) 전파 @Nonnull 하도록하는 것은 indirectPathToA(@Nonnull Integer y)이럴에게 나쁜 관행이다 : 당신이 전체 호출 스택 (당신은 추가하는 경우 등 전파를 mainain해야합니다 null에 체크를 directPathToA()하면 교체해야합니다 @Nonnull으로 @Nullable전체 호출 스택). 이는 대규모 응용 프로그램의 경우 큰 유지 관리 노력입니다.
Julien Kronegg

@Nonnull 주석은 인수의 널 검증이 귀하의 편임을 강조합니다 (널이 아닌 값을 전달해야 함). 이 방법의 책임은 아닙니다.
Alexander Drobyshevsky

@Nonnull은 null 값이이 방법에 적합하지 않은 경우에도 의미가 있습니다
Alexander Drobyshevsky

5

프로젝트에서 수행하는 작업은 "일정한 조건 및 예외"코드 검사에서 다음 옵션을 활성화하는 것입니다. null을 반환하고 주석이없는 매개 변수에 전달 된 nullable 값을보고 할 수있는 메서드에 @Nullable 주석을 제안하십시오 .
검사

활성화되면 주석이없는 모든 매개 변수가 널이 아닌 것으로 취급되므로 간접 호출에 대한 경고도 표시됩니다.

clazz.indirectPathToA(null); 

더 강력한 검사를 위해 Checker Framework가 좋은 선택 일 수 있습니다 (이 훌륭한 자습서를 참조하십시오 .
참고 : 아직 사용하지 않았으며 Jack 컴파일러에 문제가있을 수 있습니다 : 이 버그 보고서를 참조하십시오


4

Java에서는 Guava의 Optional type을 사용 합니다 . 실제 유형이기 때문에 컴파일러는 그 사용에 대해 보장합니다. 그것을 우회하고 쉽게 얻을 수 NullPointerException있지만 적어도 메소드의 서명은 인수로 기대하는 것 또는 반환 할 수있는 것을 명확하게 전달합니다.


16
이 점에주의해야합니다. 선택적은 값이 실제로 선택적인 경우에만 사용되어야하며, 해당 값이없는 경우 추가 논리를위한 결정 게이트로 사용됩니다. 선택 사항으로 Objects를 담요로 대체하고 null 검사가 존재 여부를 확인하여 요점을 놓치지 않는 것으로 남용되었습니다.
Christopher Perry

JDK 8 이상을 대상으로하는 경우 java.util.OptionalGuava 클래스 대신 사용하는 것이 좋습니다. 차이점에 대한 자세한 내용 은 구아바의 노트 / 비교 를 참조하십시오.
AndrewF

1
"널 체크는 포인트를 놓친 존재에 대한 체크로 대체되었습니다"그러면 포인트 무엇인지 자세히 설명 할 수 있습니까? 이것이 내 의견으로는 이것이 옵셔 널즈 의 유일한 이유는 아니지만 확실히 가장 크고 가장 좋은 이유입니다.
Scubbo

4

Kotlin을 사용하는 경우 컴파일러에서 이러한 Null 허용 주석을 지원하며 널이 아닌 인수가 필요한 Java 메소드에 널을 전달하지 못하게합니다. 이 질문은 원래 Java를 대상으로했지만이 Java 주석을 대상으로 하기 때문에이 Kotlin 기능에 대해 언급 했으며 " 이 주석을 보다 엄격하게 적용 및 / 또는 전파 할 수있는 방법이 있습니까?"라는 질문이 있었습니다. 이 기능은 이러한 주석을보다 엄격하게 적용합니다. 합니다.

@NotNull주석을 사용하는 Java 클래스

public class MyJavaClazz {
    public void foo(@NotNull String myString) {
        // will result in an NPE if myString is null
        myString.hashCode();
    }
}

Kotlin 클래스는 Java 클래스를 호출하고 @NotNull로 주석이 달린 인수에 대해 null을 전달합니다.

class MyKotlinClazz {
    fun foo() {
        MyJavaClazz().foo(null)
    }
}  

@NotNull주석을 적용하는 Kotlin 컴파일러 오류

Error:(5, 27) Kotlin: Null can not be a value of a non-null type String

참조 : http://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations


3
이 질문은 Kotlin이 아닌 첫 번째 태그별로 Java를 처리합니다.
seh

1
이 답변이이 질문과 관련이있는 이유는 @seh 업데이트를 참조하십시오.
Mike Rylander

2
그럴 수 있지. 그것은 Kotlin의 좋은 기능입니다. Java에 대해 배우기 위해 여기에 오는 사람들을 만족시킬 것이라고 생각하지 않습니다.
seh

그러나 매개 변수에 추가되지 않은 myString.hashCode()경우에도 액세스 는 여전히 NPE를 발생 @NotNull시킵니다. 그것을 추가하는 것에 대해 더 구체적은 무엇입니까?
kAmol

@kAmol 여기서 차이점은 Kotlin을 사용할 때 런타임 대신 컴파일 타임 오류가 발생한다는 것입니다 오류 . 주석은 개발자에게 널이 전달되지 않았 음을 확인해야 함을 알리기위한 것입니다. 런타임에 널이 전달되는 것을 막지는 않지만 널 (또는 null)로이 메소드를 호출하는 코드를 작성할 수는 없습니다. null을 반환 할 수있는 함수로).
Mike Rylander

-4

Java 8의 새로운 기능 선택 사항 이므로 더 이상 자신의 코드에서 @Nullable 또는 @Notnull을 사용해서는 안됩니다 . 아래 예제를 보자.

public void printValue(@Nullable myValue) {
    if (myValue != null) {
        System.out.print(myValue);
    } else {
        System.out.print("I dont have a value");
}

다음과 같이 다시 작성할 수 있습니다.

public void printValue(Optional<String> myValue) {
    if (myValue.ifPresent) {
        System.out.print(myValue.get());
    } else {
        System.out.print("I dont have a value");
}

선택 사항 사용하면 null 값을 강제로 확인하게됩니다 . 위 코드에서 get메소드 를 호출해야만 값에 액세스 할 수 있습니다 .

또 다른 장점은 코드가 더 읽기 쉽다는 것 입니다. Java 9 ifPresentOrElse 추가 하면 함수를 다음과 같이 작성할 수도 있습니다.

public void printValue(Optional<String> myValue) {
    myValue.ifPresentOrElse(
        v -> System.out.print(v),
        () -> System.out.print("I dont have a value"),
    )
}

로도 Optional이러한 어노테이션을 사용하는 라이브러리 및 프레임 워크가 여전히 많으므로 선택 사항을 사용하도록 업데이트 된 버전으로 모든 종속성을 업데이트 / 대체 할 수 없습니다. Optional그러나 자신의 코드에서 null을 사용하는 상황에서 도움이 될 수 있습니다.
Mike Rylander
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.