Programing

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

crosscheck 2020. 6. 22. 08:00
반응형

왜 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)에서

The exception is triggered in the LookupService class while the origin of it is well earlier (the Dictionary constructor). It makes the overall issue analysis much less obvious.
Is words null? Is words.get(0) null ? Both ? Why the one, the other or maybe both are null ? Is it a coding error in Dictionary (constructor? invoked method?) ? Is it a coding error in LookupService ? (constructor? invoked method?) ?
Finally, we will have to inspect more code to find the error origin and in a more complex class maybe even use a debugger to understand more easily what it happened.
But why a simple thing (a lack of null check) become a complex issue ?
Because we allowed the initial bug/lack identifiable on a specific component leak on lower components.
Imagine that LookupService was not a local service but a remote service or a third party library with few debugging information or imagine that you didn't have 2 layers but 4 or 5 layers of object invocations before that the null be detected ? The problem would be still more complex to analyse.

So the way to favor is :

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

In this way, no headache : we get the exception thrown as soon as this is received :

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

// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Objects.java:203)
    at com.Dictionary.(Dictionary.java:15)
    at com.Dictionary.main(Dictionary.java:24)

Note that here I illustrated the issue with a constructor but a method invocation could have the same non null check constraint.


As a side note, this fail fast before Object#requireNotNull was implemented slightly different before java-9 inside some of the jre classes themselves. Suppose the case :

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

In java-8 this compiles as (only the relevant parts)

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

Basically an operation as : yourReference.getClass - which would fail if yourRefercence is null.

Things have changed in jdk-9 where the same code compiles as

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

Or basically Objects.requireNotNull (yourReference)


The basic usage is checking and throwing NullPointerException immediately.

One better alternative (shortcut) to cater to the same requirement is @NonNull annotation by lombok.

참고URL : https://stackoverflow.com/questions/45632920/why-should-one-use-objects-requirenonnull

반응형