JDK 소스 코드를 읽을 때 작성자가 매개 변수가 null 인 경우 매개 변수를 확인한 다음 새 NullPointerException ()을 수동으로 throw하는 것이 일반적입니다. 왜 그렇게합니까? 메소드를 호출 할 때 새로운 NullPointerException ()이 발생하기 때문에 그렇게 할 필요가 없다고 생각합니다. (예를 들어 HashMap의 소스 코드는 다음과 같습니다.)
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
Node<K,V> e; V oldValue;
int hash = hash(key);
if ((e = getNode(hash, key)) != null &&
(oldValue = e.value) != null) {
V v = remappingFunction.apply(key, oldValue);
if (v != null) {
e.value = v;
afterNodeAccess(e);
return v;
}
else
removeNode(hash, key, null, false, true);
}
return null;
}
답변
기억해야 할 여러 가지 이유가 있습니다.
빠른 실패 : 실패 할 경우 나중에보다는 빨리 실패하는 것이 가장 좋습니다. 이를 통해 문제를 소스에 더 가깝게 파악하여 쉽게 식별하고 복구 할 수 있습니다. 또한 실패 할 수있는 코드에서 CPU주기 낭비를 방지합니다.
의도 : 예외를 명시 적으로 throw하면 오류가 의도적으로 있으며 작성자가 결과를 알고 있음을 유지 관리자에게 명확하게 알 수 있습니다.
일관성 : 오류가 자연스럽게 발생하도록 허용 된 경우 모든 시나리오에서 발생하지 않을 수 있습니다. 예를 들어 매핑이 없으면 remappingFunction
사용되지 않으며 예외가 발생하지 않습니다. 입력을 미리 검증하면보다 결정적인 동작과보다 명확한 문서화가 가능 합니다.
안정성 : 코드는 시간이 지남에 따라 발전합니다. 예외가 발생하는 코드는 자연스럽게 약간의 리팩토링 후에는 그렇게하지 않거나 다른 상황에서 그렇게 할 수 있습니다. 명시 적으로 던지면 행동이 실수로 변경 될 가능성이 줄어 듭니다.
답변
명확성, 일관성 및 불필요한 불필요한 작업이 수행되는 것을 방지하기위한 것입니다.
메소드 맨 위에 가드 절이 없으면 어떻게 될지 고려하십시오. 그것은 NPE가 던져지기 전에 항상 전화를 걸었 hash(key)
을 getNode(hash, key)
때조차도 null
지나갔습니다 remappingFunction
.
더 나쁜 것은 if
조건이 조건을 가지면 브랜치를 false
취하는데 else
전혀 사용 remappingFunction
하지 않기 때문에 a null
가 전달 될 때 메소드가 항상 NPE를 던지는 것은 아닙니다 . 지도의 상태에 따라 다릅니다.
두 시나리오 모두 나쁘다. 이 메서드에 null
유효한 값이 아닌 경우 remappingFunction
호출시 객체의 내부 상태에 관계없이 일관되게 예외가 발생해야하며, 그냥 던질 것이라는 점에서 의미없는 불필요한 작업을 수행하지 않고 수행해야합니다. 마지막으로, 소스 코드를 검토하는 사람이라면 누구나 그렇게 할 수 있다는 것을 쉽게 알 수 있도록 가드를 미리 확보하는 것이 깨끗하고 명확한 코드의 원칙입니다.
현재 모든 코드 분기에서 예외가 발생하더라도 향후 코드 개정으로 변경 될 수 있습니다. 처음에 검사를 수행하면 확실히 수행됩니다.
답변
@shmosel의 탁월한 답변에 나열된 이유 외에도 …
성능 : 일부 JVM의 경우 JVM이 아닌 NPE를 명시 적으로 처리하면 성능상의 이점이있을 수 있습니다.
Java 인터프리터 및 JIT 컴파일러가 널 포인터의 역 참조를 감지하는 전략에 따라 다릅니다. 한 가지 전략은 null을 테스트하지 않고 대신 명령이 주소 0에 액세스하려고 할 때 발생하는 SIGSEGV를 트랩하는 것입니다. 이는 참조가 항상 유효한 경우 가장 빠른 방법이지만 NPE의 경우 비용 이 많이 듭니다 .
null
코드에서 명시 적으로 테스트 하면 NPE가 자주 발생하는 시나리오에서 SIGSEGV 성능이 저하되는 것을 피할 수 있습니다.
(이것이 현대 JVM에서 가치있는 마이크로 최적화가 될지는 의문이지만 과거에는 가능했을 것입니다.)
호환성 : 예외에 메시지가없는 이유는 JVM 자체에서 발생하는 NPE와의 호환성 때문입니다. 호환되는 Java 구현에서 JVM에 의해 발생 된 NPE에는 null
메시지가 있습니다. (Android Java는 다릅니다.)
답변
다른 사람들이 지적한 것 외에도 여기에서 컨벤션의 역할에 주목할 가치가 있습니다. 예를 들어 C #에서는 이와 같은 경우 예외를 명시 적으로 발생시키는 것과 동일한 규칙이 있지만 구체적으로 ArgumentNullException
는 좀 더 구체적입니다. (는 C # 규칙은 것입니다 NullReferenceException
항상 아주 간단하게, 그것은 안 – 어떤 종류의 버그를 나타냅니다 적 , 부여, 생산 코드에서 발생 ArgumentNullException
일반적으로도 않지만, 그렇게하지 “의 라인을 따라 더 많은 버그가 수 라이브러리를 올바르게 사용하는 방법을 이해하십시오 ( “종류의 버그).
따라서 기본적으로 C # NullReferenceException
에서는 프로그램이 실제로 사용하려고 시도했지만 ArgumentNullException
값이 잘못되었음을 인식하여 사용하려고 시도하지 않아도됩니다. 실제 상황에 따라 의미가 다를 수 있습니다 (환경에 따라 다름). 문제 ArgumentNullException
의 방법에 아직 부작용이 없었기 때문입니다 (방법의 전제 조건이 실패 했으므로).
덧붙여, 넌 모금 무엇인가를하는 경우 ArgumentNullException
나 IllegalArgumentException
, 체크를하는 점의 부품이 있음을 : 당신은 당신이 “보통”했던 것보다 다른 예외를 원하는 얻을.
어느 쪽이든, 예외를 명시 적으로 제기하면 메소드의 사전 조건 및 예상 인수에 대해 명시 적으로 작성하는 것이 좋습니다. 따라서 코드를보다 쉽게 읽고, 사용하고, 유지할 수 있습니다. 명시 적으로 확인 null
하지 않은 경우 아무도 null
인수를 전달하지 않을 것이라고 생각했기 때문에 어쨌든 모르겠습니다. 어쨌든 예외를 throw하기 위해 계산하고 있거나 확인하는 것을 잊어 버렸습니다.
답변
따라서 나중에지도를 사용할 때 오류가 발생하지 않고 오류가 발생하자마자 예외가 발생하며 그 이유를 이해할 수 없습니다.
답변
겉보기에 불규칙한 오류 조건이 명확한 계약 위반으로 바뀝니다.이 기능에는 올바르게 작동하기위한 몇 가지 전제 조건이 있으므로 미리 확인하여 충족시켜야합니다.
그 결과 computeIfPresent()
예외가 발생했을 때 디버깅 할 필요가 없습니다 . 전제 조건 검사에서 예외가 발생하면 잘못된 인수로 함수를 호출 한 것입니다. 검사가없는 경우 computeIfPresent()
자체에 예외가 발생하는 일부 버그가있을 가능성을 배제해야합니다 .
분명히, 제네릭을 던지는 것은 NullPointerException
계약 위반 자체를 나타내지 않기 때문에 정말 나쁜 선택입니다. IllegalArgumentException
더 나은 선택이 될 것입니다.
참고 사항 :
Java가 이것을 허용하는지 알지 못하지만 (의심 할 것입니다) C / C ++ 프로그래머 assert()
는이 경우 디버깅을 사용하는 것이 훨씬 낫습니다. 조건이 거짓으로 평가됩니다. 그래서, 당신이 달렸다면
void MyClass_foo(MyClass* me, int (*someFunction)(int)) {
assert(me);
assert(someFunction);
...
}
디버거와 어떤 NULL
인수에 전달 된 것이 있으면 프로그램은 인수가 무엇 인지 알려주는 줄에서 바로 멈추고 NULL
여가 시간에 전체 호출 스택의 모든 지역 변수를 검사 할 수 있습니다.
답변
그것은 가능하기 때문입니다 하지 자연적으로 발생 할 수 있습니다. 다음과 같은 코드를 보자 :
bool isUserAMoron(User user) {
Connection c = UnstableDatabase.getConnection();
if (user.name == "Moron") {
// In this case we don't need to connect to DB
return true;
} else {
return c.makeMoronishCheck(user.id);
}
}
(물론이 샘플에는 코드 품질에 대한 수많은 문제가 있습니다. 완벽한 샘플을 상상하기 위해 게으른 죄송합니다)
상황 c
실제로 사용되지 않는 은 가능 NullPointerException
하더라도 던져지지 않습니다 c == null
.
더 복잡한 상황에서는 그러한 경우를 찾기가 쉽지 않습니다. 이것이 일반적인 점검 if (c == null) throw new NullPointerException()
이 더 나은 이유 입니다.