[java] 왜 Objects.requireNonNull ()을 사용해야합니까?

Oracle JDK의 많은 Java 8 메소드 가 주어진 객체 (인수)가 인 경우 Objects.requireNonNull()내부적으로 throw되는 많은 Java 8 메소드를 언급했습니다 .NullPointerExceptionnull

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

그러나 객체가 역 참조 NullPointerException되면 어쨌든 발생합니다 null. 그렇다면 왜이 여분의 null 검사를 수행하고 던져야
NullPointerException합니까?

명백한 대답 (또는 이점)은 코드를 더 읽기 쉽게 만들고 동의한다는 것입니다. Objects.requireNonNull()방법의 시작 부분에 사용하는 다른 이유를 알고 싶습니다
.



답변

그렇게하면 명시 적으로 만들 수 있기 때문입니다. 처럼:

public class Foo {
  private final Bar bar;

  public Foo(Bar bar) {
    Objects.requireNonNull(bar, "bar must not be null");
    this.bar = bar;
  }

또는 더 짧게 :

  this.bar = Objects.requireNonNull(bar, "bar must not be null");

이제 당신 은 알고있다 :

  • Foo 객체를 사용하여 성공적으로 생성 한 시점new()
  • 다음 필드가됩니다 보장 null 이외.

오늘 Foo 객체를 만들고 내일은 해당 필드를 사용하고 던지는 메소드를 호출합니다. 어제 생성자에게 전달 된 참조가 왜 null인지 내일 알 수 없습니다 .

다시 말해,이 방법을 명시 적으로 사용하여 들어오는 참조 를 확인 하면 예외가 발생하는 시점을 제어 할 수 있습니다 . 그리고 대부분의 경우 가능한 빨리 실패 하고 싶습니다 !

주요 장점은 다음과 같습니다.

  • 말했듯이 통제 된 행동
  • 더 쉬운 디버깅-객체 생성 컨텍스트에서 포기하기 때문입니다. 당신이 당신의 로그 / 추적이 잘못 된 것을 말해 줄 수있는 특정 시점에!
  • 그리고 위에서 보여 지듯이 :이 아이디어의 진정한 힘은 최종 필드 와 함께 전개 됩니다. 이제 클래스의 다른 코드barnull이 아니라고 가정 할 수 있으므로 if (bar == null)다른 곳에서 검사 할 필요가 없습니다 !

답변

빠른 실패

가능한 빨리 코드가 충돌해야합니다. 작업의 절반을 수행하지 말고 널과 충돌을 역 참조하면 일부 작업의 절반 만 남겨두고 시스템이 유효하지 않은 상태가됩니다.

이것을 일반적으로 “fail early”또는 “fail-fast”라고 합니다.


답변

그러나 null 객체가 역 참조되면 NullPointerException이 발생합니다. 그렇다면 왜이 여분의 null 검사를 수행하고 NullPointerException을 발생시켜야합니까?

그것은 당신이 문제를 감지 의미 즉시신뢰성 .

치다:

  • 코드가 이미 몇 가지 부작용을 수행 한 후에는 메소드의 뒷부분에서 참조를 사용할 수 없습니다
  • 이 방법에서는 참조가 전혀 역 참조되지 않을 수 있습니다
    • 완전히 다른 코드로 전달 될 수 있습니다 (즉, 코드 공간에서 원인과 오류가 멀리 떨어져 있음)
    • 훨씬 나중에 사용할 수 있습니다 (예 : 원인과 오류는 시간이 많이 남음)
  • null 참조 유효하지만 의도하지 않은 효과가있는 곳에서 사용될 수 있습니다.

.NET은 분리하여 더 나은 만드는 NullReferenceException에서 ( “당신이 널 (null) 값을 역 참조”) ArgumentNullException( “당신은 인수로 null을 통과하지 않아야 – 그것은 위해이었다 매개 변수) 나 자바 같은 일을했다 좋겠지 만, 심지어와. 단지는 NullPointerException, 그것은 여전히 많은 오류가 그것을 검출 할 수있는 가장 빠른 시점에서 발생 된 경우 수정 코드에 쉽게.


답변

requireNonNull()메소드에서 첫 번째 명령문으로 사용하면 지금 예외의 원인을 식별 / 빠르게 식별 할 수 있습니다.
스택 트레이스는 호출자가 요구 사항 / 계약을 존중하지 않기 때문에 메소드가 입력되는 즉시 예외가 발생했음을 명확하게 나타냅니다 .
지나가는 null다른 방법으로 목적하는 것은 할 수 참으로 한 번에 예외하지만 문제의 원인이 더 많은 예외가의 특정 호출에 던져 질 것이다로 이해하는 복잡 할 수있다 도발 null훨씬 더 할 수있다 개체를.


여기에 우리가 일반적으로 실패를 선호해야하는 이유를 보여주는 구체적이고 실제적인 예가 Object.requireNonNull()있습니다 null.

에 포함 된 단어 를 나타내는 Dictionarya LookupService와 a 를 구성 하는 클래스를 가정하십시오 . 이러한 필드는 설계되지 않았 으며 이들 중 하나는 ListStringnullDictionary 생성자에 .

이제 메소드 엔트리 (여기서 생성자) Dictionarynull점검 하지 않고 “나쁜”구현을 가정 해 보자 .

public class Dictionary {

    private final List<String> words;
    private final LookupService lookupService;

    public Dictionary(List<String> words) {
        this.words = this.words;
        this.lookupService = new LookupService(words);
    }

    public boolean isFirstElement(String userData) {
        return lookupService.isFirstElement(userData);
    }
}


public class LookupService {

    List<String> words;

    public LookupService(List<String> words) {
        this.words = words;
    }

    public boolean isFirstElement(String userData) {
        return words.get(0).contains(userData);
    }
}

이제 매개 변수에 Dictionary대한 null참조를 사용하여 생성자를 호출 해 보겠습니다 words.

Dictionary dictionary = new Dictionary(null);

// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");

JVM은 다음 명령문에서 NPE를 발생시킵니다.

return words.get(0).contains(userData); 
스레드 "main"의 예외 java.lang.NullPointerException
    LookupService.isFirstElement (LookupService.java:5)에서
    Dictionary.isFirstElement (Dictionary.java:15)에서
    Dictionary.main (Dictionary.java:22)에서

예외는 LookupService클래스 에서 발생 하며 그 기원은 훨씬 빠릅니다 ( Dictionary생성자). 전체 이슈 분석이 훨씬 덜 명확 해집니다.
입니까 words null? 입니까 words.get(0) null? 둘 다? 왜 하나, 다른 하나 또는 둘 다 null인가? Dictionary(생성자? 호출 된 메소드?) 의 코딩 오류 입니까? 의 코딩 오류 LookupService입니까? (생성자? 호출 된 메소드?)?
마지막으로, 오류 원점을 찾기 위해 더 많은 코드를 검사해야하며, 더 복잡한 클래스에서는 디버거를 사용하여 발생한 상황을 더 쉽게 이해할 수도 있습니다.
그러나 왜 간단한 것 (널 체크가 없음)이 복잡한 문제가됩니까?
하위 구성 요소의 특정 구성 요소 누출에서 식별 가능한 초기 버그 / 부족을 허용했기 때문입니다.
상상 해봐LookupService로컬 서비스가 아니라 원격 서비스 또는 디버깅 정보가 거의없는 타사 라이브러리 였거나 null감지 되기 전에 2 층이 아니라 4 또는 5 층의 객체 호출이 있다고 상상해보십시오 . 문제는 여전히 분석하기가 더 복잡합니다.

따라서 선호하는 방법은 다음과 같습니다.

public Dictionary(List<String> words) {
    this.words = Objects.requireNonNull(words);
    this.lookupService = new LookupService(words);
}

이런 식으로 두통이 발생하지 않습니다.이를 수신하자마자 예외가 발생합니다.

// exception thrown early : in the constructor 
Dictionary dictionary = new Dictionary(null);

// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
스레드 "main"의 예외 java.lang.NullPointerException
    java.util.Objects.requireNonNull (Objects.java:203)에서
    com.Dictionary. (Dictionary.java:15)에서
    com.Dictionary.main (Dictionary.java:24)에서

여기서는 생성자와 관련된 문제를 설명했지만 메서드 호출은 null이 아닌 동일한 검사 제약 조건을 가질 수 있습니다.


답변

참고로, 이것은 Object#requireNotNull일부 jre 클래스 자체에서 java-9 이전에 약간 다르게 구현 되기 전에 빠르게 실패 합니다. 사례를 가정하십시오.

 Consumer<String> consumer = System.out::println;

Java-8에서는 다음과 같이 컴파일됩니다 (관련 부분 만)

getstatic Field java/lang/System.out
invokevirtual java/lang/Object.getClass

기본적으로 다음과 같은 작업은 yourReference.getClass-Refercence가 실패하면 실패합니다 null.

동일한 코드가 다음과 같이 컴파일되는 jdk-9에서 상황이 변경되었습니다.

getstatic Field java/lang/System.out
invokestatic java/util/Objects.requireNonNull

아니면 기본적으로 Objects.requireNotNull (yourReference)


답변

기본적인 사용법은 NullPointerException바로 확인하고 던지는 것 입니다.

동일한 요구 사항을 충족시키는 더 좋은 대안 (바로 가기)은 lombok의 @NonNull 주석입니다.


답변

null나중에 있는 개체의 멤버에 액세스하면 Null 포인터 예외가 발생 합니다. Objects.requireNonNull()즉시 값을 확인하고 진행하지 않고 즉시 예외를 발생시킵니다.