Java에서 null을 처리하는 가장 좋은 방법은 무엇입니까? [닫은]


21

NullPointerException으로 인해 실패하는 코드가 있습니다. 객체가 존재하지 않는 객체에서 메소드가 호출되고 있습니다.

그러나 이것은 이것을 해결하는 가장 좋은 방법에 대해 생각하게했습니다. null 포인터 예외에 대한 향후 증거 코드를 보장하기 위해 항상 null을 방어 적으로 코딩합니까, 아니면 null의 원인을 수정하여 다운 스트림에서 발생하지 않도록해야합니다.

당신의 생각은 무엇입니까?


5
왜 "널 (null)의 원인을 고치지"않겠습니까? 이것이 유일한 합리적인 선택이 아닌 이유의 예를 제공 할 수 있습니까? 분명히 무언가가 당신을 명백한 것으로부터 멀어지게해야합니다. 무엇입니까? 근본 원인을 수정하지 않는 것이 왜 첫 번째 선택입니까?
S.Lott

문제의 "근본 원인"을 수정하면 단기적으로 도움이 될 수 있지만 코드가 여전히 방어 적으로 null을 확인하지 않고 null을 유발할 수있는 다른 프로세스에서 재사용하는 경우 다시 수정해야합니다.
Shaun F

2
@Shaun F : 코드가 깨지면 깨졌습니다. 코드에서 예기치 않은 null을 생성하고 수정되지 않는 방법을 알 수 없습니다. 분명히, "멍청한 관리"또는 "정치적"일이 진행되고 있습니다. 또는 잘못된 코드를 어떻게 든 수용 할 수있는 것입니다. 잘못된 코드가 수정되지 않는 방법을 설명 할 수 있습니까? 이 질문을하게 만드는 정치적 배경은 무엇입니까?
S.Lott

1
"널에 널이있는 데이터 생성에 취약하다"무엇을 의미 하는가? "데이터 생성 루틴을 수정할 수 있습니다"라는 취약점이 더 이상 존재하지 않습니다. 이 "나중에 널이있는 데이터 생성"이 무엇을 의미하는지 이해할 수 없습니다. 버기 소프트웨어?
S.Lott

1
@ S.Lott, 캡슐화 및 단일 책임. 모든 코드가 다른 모든 코드의 기능을 "알지"않습니다. 클래스가 Froobinators를 수락하면 Froobinator를 만든 사람과 방법에 대해 가능한 적은 가정을합니다. "외부"코드에서 나왔거나 코드베이스의 다른 부분에서 나왔습니다. 버그가있는 코드를 수정해서는 안된다는 말은 아닙니다. "방어 프로그래밍"을 검색하면 OP가 참조하는 내용을 찾을 수 있습니다.
Paul Draper

답변:


45

널이 메소드에 적합한 입력 매개 변수 인 경우 메소드를 수정하십시오. 그렇지 않은 경우 발신자를 수정하십시오. "합리적"은 유연한 용어이므로 다음 테스트를 제안합니다. 메소드가 어떻게 널 입력을 처리해야합니까? 둘 이상의 가능한 답변을 찾으면 null은 합리적인 입력 이 아닙니다 .


1
정말 간단합니다.
biziclop

3
Guava에는 null 확인을 간단하게 만드는 매우 유용한 도우미 메서드가 Precondition.checkNotNull(...)있습니다. stackoverflow.com/questions/3022319/…를

내 규칙은 가능한 경우 모든 것을 합리적인 기본값으로 초기화하고 나중에 null 예외에 대해 걱정하는 것입니다.
davidk01

Thorbjørn : 우발적 인 NullPointerException을 의도적 NullPointerException으로 대체합니까? 경우에 따라 조기 점검을 수행하는 것이 유용하거나 필요할 수도 있습니다. 그러나 나는 가치가 거의없고 프로그램을 더 크게 만드는 무의미한 상용구 null 검사가 두려워합니다.
user281377

6
null이 메서드에 대한 합리적인 입력 매개 변수는 아니지만 메서드 호출이 실수로 null을 전달할 가능성이있는 경우 (특히 메서드가 공용 인 경우) IllegalArgumentExceptionnull이 주어질 때 메서드가 throw되도록 할 수도 있습니다 . 이는 메소드 호출자에게 버그가 메소드 자체가 아니라 코드에 있음을 나타냅니다.
Brian

20

null을 사용하지 말고 옵션을 사용하십시오.

지적했듯이 nullJava에서 가장 큰 문제 중 하나는 모든 곳 에서 또는 적어도 모든 참조 유형에 사용할 수 있다는 것 입니다.

할 수있는 null것과 할 수없는 것을 말하는 것은 불가능 합니다.

Java 8에는 훨씬 더 나은 패턴이 도입되었습니다 Optional.

그리고 오라클의 예 :

String version = "UNKNOWN";
if(computer != null) {
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null) {
    USB usb = soundcard.getUSB();
    if(usb != null) {
      version = usb.getVersion();
    }
  }
}

이들 각각이 성공적인 값을 리턴하거나 리턴하지 않을 경우 API를 Optionals로 변경할 수 있습니다 .

String name = computer.flatMap(Computer::getSoundcard)
    .flatMap(Soundcard::getUSB)
    .map(USB::getVersion)
    .orElse("UNKNOWN");

유형에서 선택성을 명시 적으로 인코딩하면 인터페이스가 훨씬 좋아지고 코드가 깨끗해집니다.

Java 8을 사용하지 않는 경우 com.google.common.base.OptionalGoogle Guava에서 볼 수 있습니다 .

Guava 팀의 좋은 설명 : https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained

여러 언어의 예를 포함하여 null에 대한 단점에 대한 더 일반적인 설명 : https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/


@Nonnull, @Nullable

Java 8은 이러한 주석을 추가하여 IDE와 같은 코드 검사 도구가 문제를 포착하는 데 도움을줍니다. 그들은 효과가 상당히 제한적입니다.


말이되는지 확인

특히 코드에서 null값으로 수행 할 수있는 작업이없는 경우 50 %의 null 검사 코드를 작성하지 마십시오 .

반면에, null사용될 수 있고 의미가 있다면 그것을 사용하십시오.


궁극적 null으로 Java에서 제거 할 수는 없습니다 . Optional가능 하면 추상화를 대체하고 null합리적인 시간에 다른 것을 할 수 있는지 확인 하는 것이 좋습니다 .


2
일반적으로 4 년 된 질문에 대한 답변은 품질이 낮아서 삭제됩니다. Java 8 (이전에는 존재하지 않았던)이 어떻게 문제를 해결할 수 있는지에 대해 잘 이야기했습니다.

물론 원래의 질문과 관련이 없습니다. 그리고이 주장이 실제로 선택 사항이라고 할 수있는 것은 무엇입니까?
jwenting

5
@jwenting 원래 질문과 완전히 관련이 있습니다. "드라이버로 못을 박는 가장 좋은 방법"은 "드라이버 대신 해머를 사용하는 것"입니다.
데니스

@jwenting, 나는 그것을 덮었다 고 생각하지만, 명확성을 위해 : 인수는 선택 사항이 아니며, 일반적으로 null을 확인하지 않습니다. 100 줄의 null 검사와 40 줄의 물질이 있습니다. 더 많은 공용 인터페이스에서 또는 null이 실제로 의미가있는 경우 null을 확인하십시오 (예 : 인수는 선택 사항).
Paul Draper

4
@ J-Boss, Java에서 어떻게 확인되지 않았NullPointerException 습니까? A는 NullPointerException그대로 일어날 수 있습니다 때마다 당신은 자바에서 인스턴스 메서드를 호출합니다. 당신은 것 throws NullPointerException이제까지 거의 모든 하나의 방법이다.
Paul Draper

8

이를 처리하는 여러 가지 방법이 있으며 코드를 페퍼 링하는 if (obj != null) {}것은 이상적이지 않으며, 지저분하며, 유지 보수주기 동안 코드를 읽을 때 노이즈를 추가 하고이 보일러 플레이트 래핑을 잊어 버릴 수 있기 때문에 오류가 발생하기 쉽습니다.

코드가 자동으로 계속 실행되거나 실패하는지 여부에 따라 다릅니다. 는 IS null오류 또는 예상되는 상태.

널이란?

모든 정의와 사례에서 Null 은 데이터의 절대적 부족을 나타냅니다. 데이터베이스의 널은 해당 열에 대한 값이 없음을 나타냅니다. 널은 String빈과 동일하지 않습니다 String널은, int이론적으로, ZERO과 같은 것은 아니다. 실제로는 "의존한다". Empty String는 비즈니스 로직에 의존 Null Object하기 Integer때문에 String 클래스에 대한 좋은 구현을 만들 수 있습니다 .

대안은 다음과 같습니다.

  1. Null Object패턴. null상태 를 나타내는 객체의 인스턴스 를 만들고 Null구현에 대한 참조로 해당 유형에 대한 모든 참조를 초기화하십시오 . 이는 유효한 상태 일 수도 있고 null예상되는 다른 객체에 대한 참조가 많지 않은 단순 값 유형 객체에 유용합니다 null.

  2. Aspect Oriented 도구를 사용 Null Checker하여 매개 변수가 널이되는 것을 방지 하는 aspect로 메소드를 짜십시오 . 이 경우에 인 null오류가 발생합니다.

  3. 사용 assert()하지 훨씬 더보다는 if (obj != null){}하지만, 적은 소음.

  4. Contracts For Java 와 같은 Contract Enforcement 도구를 사용하십시오 . AspectJ와 같은 유스 케이스이지만 최신 버전이며 외부 구성 파일 대신 주석을 사용합니다. Aspects와 Asserts의 두 작품 중 최고입니다.

1은 들어오는 데이터가 알려져 있고 null일부 기본값으로 대체해야 할 때 이상적인 솔루션으로 , 업스트림 소비자가 모든 null 점검 상용구 코드를 처리 할 필요가 없습니다. 알려진 기본값을 확인하는 것이 더욱 표현력이 좋습니다.

2, 3 및 4는 편리한 대체 예외 생성기 NullPointerException입니다.

결국

nullJava에서 거의 모든 경우에 논리 오류입니다. 의 근본 원인을 제거하기 위해 항상 노력해야합니다 NullPointerExceptions. null조건을 비즈니스 로직으로 사용하지 않도록 노력해야합니다 . if (x == null) { i = someDefault; }기본 오브젝트 인스턴스에 초기 어설 션을 작성하십시오.


가능한 경우 널 오브젝트 패턴이 방법 인 경우 널 케이스를 처리하는 가장 좋은 방법입니다. 생산 과정에서 물건을 날려 버릴 널을 원하는 경우는 거의 없습니다!
Richard Miskin

@Richard : null예기치 않은 경우가 아니라면 진정한 오류이므로 모든 것이 완전히 중단되어야합니다.

데이터베이스와 프로그래밍 코드에서 널 (null)은 한 가지 중요한 측면에서 다르게 처리됩니다. 프로그래밍에서 null == null(PHP에서도), 그러나 데이터베이스에서는 데이터베이스에서 "아무것도 아닌" 알 수없는 값을 null != null나타 내기 때문에 데이터베이스에서는 그렇지 않습니다. 두 개의 미지수는 반드시 동일하지는 않지만 두 개의 미지수는 동일하지 않습니다.
Simon Forsberg

8

null 검사를 추가하면 테스트에 문제가 발생할 수 있습니다. 이 위대한 강의를 참조하십시오 ...

Google Tech 강의 강의 : "깨끗한 코드 강의 – 사물을 찾지 마십시오!" 그는 24 분쯤에 그것에 대해 이야기합니다

http://www.youtube.com/watch?v=RlfLCWKxHJ0&list=PL693EFD059797C21E

편집증 프로그래밍은 모든 곳에서 널 검사를 추가하는 것을 포함합니다. 그러나 처음에는 테스트 관점에서 좋은 아이디어처럼 보이므로 null 검사 유형 테스트를 처리하기가 어렵습니다.

또한 다음과 같은 일부 객체의 존재에 대한 전제 조건을 만들 때

class House(Door door){

    .. null check here and throw exception if Door is NULL
    this.door = door
}

예외가 발생하므로 하우스를 만들 수 없습니다. 테스트 케이스가 Door 이외의 것을 테스트하기 위해 모의 객체를 생성한다고 가정하면 문이 필요하기 때문에이 작업을 수행 할 수 없습니다

모의 창조 지옥을 겪은 사람들은 이러한 유형의 성가심을 잘 알고 있습니다.

요약하면, 테스트 스위트는 문, 집, 지붕 등을 편집 할 필요없이 테스트 할 수있을 정도로 견고해야합니다. 놀랍게도 테스트에서 특정 객체에 대해 null check 테스트를 추가하는 것이 얼마나 어려운가 :)

작동하는 것이 아니라 여러 가지 테스트가 있기 때문에 항상 작동하는 응용 프로그램을 선호해야합니다.


4

tl; dr- 예상치 못한 nulls를 확인하는 것이 좋지만 응용 프로그램이 제대로 작동하도록하려면 BAD입니다.

세부

null메소드에 대한 유효한 입력 또는 출력이있는 상황 과 그렇지 않은 상황이 있습니다.

규칙 # 1 :

null매개 변수 를 허용 하거나 null값을 리턴 하는 메소드의 javadoc 은이 명확하게 문서화하고 null의미를 설명해야합니다 .

규칙 # 2 :

null적절한 이유 가없는 한 API 메소드를 승인하거나 리턴하는 것으로 지정해서는 안됩니다 .

메소드의 "계약"에 대한 명확한 스펙이 주어지면, 원하지 않는 곳 을 전달하거나 반환하는 null것은 프로그래밍 오류null 입니다.

규칙 # 3 :

응용 프로그램은 "좋은"프로그래밍 오류를 시도해서는 안됩니다.

메소드 null가 존재하지 않아야 하는 것을 감지하면 다른 것으로 바꾸어 문제를 해결하려고 시도해서는 안됩니다. 그것은 프로그래머로부터 문제를 숨 깁니다. 대신 NPE가 발생하고 장애를 유발하여 프로그래머가 근본 원인을 파악하고 해결할 수 있도록해야합니다. 테스트 중에 실패가 발견되기를 바랍니다. 그렇지 않다면 테스트 방법론에 대해 이야기합니다.

규칙 # 4 :

가능한 경우 코드를 작성하여 프로그래밍 오류를 조기에 감지하십시오.

코드에 NPE가 많이 발생하는 버그가있는 경우 가장 어려운 점은 null값의 출처를 알아낼 수 있습니다 . 진단을보다 쉽게하는 한 가지 방법은 코드를 작성하여 null가능한 빨리 감지 되도록하는 것 입니다. 종종 다른 검사와 함께이 작업을 수행 할 수 있습니다. 예 :

public setName(String name) {
    // This also detects `null` as an (intended) side-effect
    if (name.length() == 0) {
        throw new IllegalArgumentException("empty name");
    }
}

(규칙 3과 4가 현실적으로 강화되어야하는 경우가 있습니다. 예를 들어 (규칙 3), 일부 종류의 응용 프로그램 아마도 프로그래밍 오류를 감지 한 후에 계속 시도 해야 합니다. (규칙 4) 잘못된 매개 변수를 너무 많이 검사하면 성능 영향.)


3

방어적인 방법으로 수정하는 것이 좋습니다. 예를 들어 :

String go(String s){  
    return s.toString();  
}

이 줄을 따라야합니다.

String go(String s){  
    if(s == null){  
       return "";  
    }     
    return s.toString();  
}

나는 이것이 절대적으로 사소한 것을 알고 있지만, 호출자가 객체를 기대하면 null을 전달하지 않는 기본 객체를 제공하십시오.


10
이는 호출자 (이 API에 대해 코딩하는 프로그래머)가 "기본"동작을 얻기 위해 매개 변수로 null을 전달하는 습관을 개발할 수 있다는 부작용이 있습니다.
Goran Jovic

2
이것이 Java 인 경우 전혀 사용하지 않아야 new String()합니다.
biziclop

1
@biziclop은 실제로 대부분의 인식보다 훨씬 중요합니다.
Woot4Moo

1
제 생각에는 주장이 null 인 경우 주장을하고 예외를 던지는 것이 더 합리적입니다. 왜냐하면 그것이 분명히 호출자의 실수이기 때문입니다. 발신자 실수를 조용히 "수정"하지 마십시오. 대신, 그들에게 알리십시오.
Andres F.

1
@ Woot4Moo 따라서 예외를 발생시키는 자신의 코드를 작성하십시오. 중요한 것은 발신자에게 전달 된 인수가 잘못되었음을 알리고 가능한 빨리 알려주는 것입니다. nulls를 자동으로 수정 하는 것이 NPE를 던지는 것보다 최악의 가능한 옵션입니다.
Andres F.

2

null에 대한 다음과 같은 일반적인 규칙은 지금까지 많은 도움이되었습니다.

  1. 데이터가 제어 시스템 외부에서 오는 경우, 체계적으로 널 (null)을 점검하고 적절하게 작동하십시오. 이것은 함수에 의미가있는 예외를 던지는 것을 의미합니다 (체크 또는 체크되지 않음, 예외의 이름이 정확히 무슨 일인지 알려줍니다). 그러나 잠재적으로 놀라움을 줄 수있는 시스템의 가치를 잃어 버리지 마십시오.

  2. Null이 데이터 모델에 적합한 값의 도메인 내에 있으면 적절하게 처리하십시오.

  3. 값을 반환 할 때 가능하면 null을 반환하지 마십시오. 항상 빈 목록, 빈 문자열, Null 개체 패턴을 선호합니다. 주어진 유스 케이스에 대해 가능한 최상의 데이터 표현 인 경우 널을 리턴 값으로 유지하십시오.

  4. 아마도 가장 중요한 것은 ... 테스트, 테스트 및 다시 테스트하십시오. 코드를 테스트 할 때 코드를 코더로 테스트하지 마십시오. Nazi psychopath 지배자로 테스트하고 해당 코드에서 지옥을 고문하는 모든 종류의 방법을 상상해보십시오.

이것은 널과 관련하여 편집증 측면에서 약간의 경향이 있으며 종종 시스템을 외부 세계와 인터페이스하고 내부의 값을 풍부한 중복으로 엄격하게 제어하는 ​​외관 및 프록시로 이어집니다. 외부 세계는 내가 직접 코딩하지 않은 모든 것을 의미합니다. 비용 런타임을 부담하지만 지금까지 코드의 "널 안전 섹션"을 생성하여이를 최적화 할 필요는 거의 없었습니다. 나는 건강 관리를 위해 오래 실행되는 시스템을 주로 만들고 있다고 말해야합니다. 마지막으로 원하는 것은 다른 시스템의 누군가가 이름을 알 수 없기 때문에 예기치 않은 널 포인터로 인해 요오드 알레르기를 CT 스캐너에 전달하는 인터페이스 하위 시스템입니다 아포스트로피 또는 但 耒耨。와 같은 문자를 포함

어쨌든 .. 내 2 센트


1

기능적 언어의 Option / Some / None 패턴 사용을 제안합니다. Java 전문가는 아니지만 C # 프로젝트에서이 패턴을 직접 구현하여 Java 세계로 변환 할 수 있다고 확신합니다.

이 패턴의 기본 개념은 다음과 같습니다. 논리적으로 값이 없을 가능성이있는 상황 (예 : id를 데이터베이스에서 검색 할 때)이있는 경우 Option [T] 유형의 오브젝트를 제공합니다. 여기서 T는 가능한 값입니다. . None [T] 클래스의 값 객체가 없으면 값을 포함하는 값-Some [T]의 객체가 반환 된 경우 반환됩니다.

이 경우 가치 부재 가능성을 처리 해야하며 코드 검토를 수행하면 잘못 처리 한 장소를 쉽게 찾을 수 있습니다. C # 언어 구현에서 영감을 얻으려면 내 비트 버킷 저장소 https://bitbucket.org/mikegirkin/optionsomenone을 참조하십시오.

널값을 리턴하고 논리적으로 오류와 같거나 (예 : 파일이 없거나 연결할 수없는 경우) 예외를 발생 시키거나 다른 오류 처리 패턴을 사용해야합니다. 이 뒤에 숨겨진 아이디어 는 가치 결여 상황을 처리 해야 하고 코드에서 잘못된 처리 위치를 쉽게 찾을 수있을 때 솔루션으로 끝납니다 .


0

글쎄, 메소드의 가능한 결과 중 하나가 null 값이라면 방어 적으로 코딩해야하지만 메소드가 null이 아닌 값을 반환해야하지만 확실하지는 않습니다.

평소와 같이 그것은 인생의 대부분의 것들과 마찬가지로 경우에 달려 있습니다 :)



0
  1. 선택적인 유형이 아니라면 선택적 유형을 사용하지 마십시오. 옵션으로 null을 기대하지 않았고 정기적으로 버그가있는 코드를 작성하지 않기 때문에 종료는 예외로 처리하는 것이 좋습니다.

  2. 구글의 기사가 지적했듯이 문제는 그것이 사용하는 유형으로 null이 아닙니다. 문제는 널 (null)을 점검하고 처리해야한다는 것입니다. 종종 정상적으로 종료 될 수 있습니다.

  3. 프로그램의 일상적인 작업 이외의 영역 (유효하지 않은 사용자 입력, 데이터베이스 문제, 네트워크 장애, 파일 누락, 데이터 손상)에서 유효하지 않은 조건을 나타내는 다수의 null 경우가 있습니다. 로그 만하면됩니다.

  4. 예외 처리는 선택과 같은 유형과 달리 JVM에서 다른 작동 우선 순위와 특권이 허용됩니다. 처리기의 메모리에 우선 순위가 지정된 지연로드를 조기에 지원하는 것을 포함하여 발생의 예외적 인 특성에 적합합니다. 항상 매달려 있어야합니다.

  5. 널 핸들러를 어디서나 작성할 필요는 없으며, 잠재적으로 신뢰할 수없는 데이터 서비스에 액세스 할 수있는 곳에서만 발생할 수 있으며, 대부분 쉽게 추상화 할 수있는 몇 가지 일반적인 패턴에 속하므로 실제로 필요한 것만 필요합니다 드문 경우를 제외하고 핸들러 호출

따라서 내 대답은 확인 된 예외로 감싸서 처리하거나 신뢰할 수없는 코드가 기능 범위 내에 있으면 수정하는 것 같습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.