! = null 문 피하기


4014

나는 object != null피하기 위해 많이 사용 합니다 NullPointerException.

이것에 대한 좋은 대안이 있습니까?

예를 들어 자주 사용합니다.

if (someobject != null) {
    someobject.doCalc();
}

A에 대한이 확인 NullPointerException에 대한 someobject위의 코드 조각의 객체입니다.

허용되는 답변이 최신 정보가 아닐 수 있습니다 . 최신 접근 방식 은 https://stackoverflow.com/a/2386013/12943 을 참조 하십시오 .


120
@Shervin Encouraging nulls는 코드를 이해하기 어렵고 덜 안정적으로 만듭니다.
Tom Hawtin-tackline

44
Elvis 연산자가 제안 되었지만 Java 7에 없는 것처럼 보입니다 . 너무 나쁘지? ? :와? []는 시간을 크게 절약 해줍니다.
Scott

72
null을 사용하지 않는 것이 다른 대부분의 제안보다 우수합니다. 예외를 던지거나 null을 반환하거나 허용하지 마십시오. BTW- 'assert'키워드는 기본적으로 비활성화되어 있기 때문에 쓸모가 없습니다. 항상 사용 가능한 실패 메커니즘 사용
ianpojman

13
이것이 제가 스칼라를 사용하는 이유 중 하나입니다. 스칼라에서는 모든 것이 널 입력 가능하지 않습니다. "아무것도"를 전달하거나 반환하려면 T als 인수 또는 반환 유형 대신 Option [T]를 명시 적으로 사용해야합니다.
Thekwasti

11
@thSoft 실제로 스칼라의 멋진 Option유형은 언어가 통제하거나 거부하려는 의지로 인해 손상되었습니다 null. 나는 hackernews에서 이것을 언급했으며 "어쨌든 스칼라에서 아무도 null을 사용하지 않는다"고 공감했다. 동료가 작성한 스칼라에서 이미 null을 찾고 있습니다. 그렇습니다, 그들은 "잘못하고 있습니다"그리고 그것에 대해 교육을 받아야하지만, 사실은 언어의 타입 시스템이 나를 보호해야한다는 사실이 남아 있습니다. 그러나 그럴 수 없습니다 :(
Andres F.

답변:


2642

이것은 저에게 중급 개발자가 중간 시점에서 직면하는 경향이있는 합리적으로 일반적인 문제처럼 들립니다. 또한 자체 코드를 작성할 때 null을 반환하여 호출자가 null을 확인 해야하는 것을 나타내는 경향이 있습니다.

다른 방법으로 null 검사가 나타나는 두 가지 경우가 있습니다.

  1. 계약에있어서 유효한 응답이 null 인 경우 과

  2. 올바른 응답이 아닌 곳.

(2) 쉽다. 어느 사용 assert또는 진술 (주장)은 (예를 들어, 실패 할 수 NullPointerException이 ). 어설 션은 1.4에서 추가 된 잘 사용되지 않는 Java 기능입니다. 구문은 다음과 같습니다.

assert <condition>

또는

assert <condition> : <object>

여기서 <condition>부울 표현식이며 메소드 출력이 오류에 포함 <object>되는 오브젝트입니다 toString().

assert문은 발생 Error( AssertionError조건이 참이 아닌 경우)를. 기본적으로 Java는 어설 션을 무시합니다. 옵션 -ea을 JVM 에 전달하여 어설 션을 사용할 수 있습니다 . 개별 클래스 및 패키지에 대한 어설 션을 활성화 및 비활성화 할 수 있습니다. 즉, 테스트에서 어설 션의 성능에 영향을 미치지 않는 것으로 나타 났지만 프로덕션 환경에서 어설 션으로 코드의 유효성을 검사하고 비활성화 할 수 있습니다.

이 경우 어설 션을 사용하지 않으면 코드가 실패하기 때문에 어설 션을 사용하면 발생합니다. 단 한가지 차이점은 어설 션을 사용하면 더 의미있는 방식으로 추가 정보를 사용하여 더 빨리 발생할 수 있다는 것입니다. 예상하지 못한 경우 왜 그런 일이 발생했는지 파악하는 데 도움이 될 수 있습니다.

(1) 조금 더 어렵다. 호출하는 코드를 제어 할 수 없으면 문제가 발생한 것입니다. null이 유효한 응답 인 경우 확인해야합니다.

그러나 제어하는 ​​코드 (이 경우는 종종 있음) 인 경우 다른 이야기입니다. 응답으로 널을 사용하지 마십시오. 컬렉션을 반환하는 메서드를 사용하면 쉽습니다. 거의 항상 null 대신 빈 컬렉션 (또는 배열)을 반환합니다.

컬렉션이 아닌 경우 어려울 수 있습니다. 이러한 인터페이스를 예로 들면 다음과 같이 고려하십시오.

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

파서는 원시 사용자 입력을 취하고 무언가에 대한 명령 행 인터페이스를 구현하는 경우해야 할 일을 찾습니다. 적절한 조치가 없으면 계약이 널을 리턴하도록 만들 수 있습니다. 그것은 당신이 말하는 null 점검을 이끈다.

다른 해결책은 null을 반환하지 않고 대신 Null Object 패턴을 사용하는 것 입니다 .

public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}

비교:

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}

ParserFactory.getParser().findAction(someInput).doSomething();

더 간결한 코드로 이어지기 때문에 훨씬 더 나은 디자인입니다.

즉, findAction () 메소드가 의미있는 오류 메시지 (특히이 경우 사용자 입력에 의존하는 경우)와 함께 예외를 발생시키는 것이 전적으로 적합 할 것입니다. findAction 메소드가 설명없이 간단한 NullPointerException으로 날아가는 것보다 Exception을 던지는 것이 훨씬 좋습니다.

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

또는 시도 / 잡기 메커니즘이 너무 추악하다고 생각되면 아무 것도하지 말고 기본 동작이 사용자에게 피드백을 제공해야합니다.

public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}

631
DO_NOTHING 조치에 대한 귀하의 진술에 동의하지 않습니다. find action 메소드가 조치를 찾을 수 없으면 널 리턴이 올바른 조치입니다. 코드에서 실제로 발견되지 않은 동작을 "발견"하여 메소드의 원칙을 위반하여 유용한 동작을 찾습니다.
MetroidFan2002

266
Java에서 특히 목록과 같이 null이 과도하게 사용된다는 데 동의합니다. 너무 많은 API는 null 대신 빈 목록 / 배열 / 컬렉션을 반환하면 더 좋습니다. 여러 번 예외를 throw해야하는 경우 null이 사용됩니다. 파서가 구문 분석 할 수없는 경우 예외가 발생해야합니다.
Laplie Anderson

92
후자의 예는 널 오브젝트 디자인 패턴 인 IIRC입니다.
Steven Evers

62
@Cshah (및 MetroidFan2002). 간단하게, 계약서에 넣은 다음, 발견되지 않은 반품 조치는 아무것도하지 않을 것임이 분명합니다. 이 정보가 호출자에게 중요한 정보 인 경우 발견되지 않은 조치임을 발견하는 방법을 제공하십시오 (예 : 결과가 DO_NOTHING 오브젝트인지 확인하는 메소드 제공). 또는 액션을 정상적으로 찾아야하는 경우 여전히 null을 반환하지 말고 해당 조건을 구체적으로 나타내는 예외를 throw하십시오. 그래야 더 나은 코드가 생성됩니다. 원하는 경우 부울을 리턴하는 별도의 메소드를 제공하여 조치가 존재하는지 확인하십시오.
Kevin Brock

35
간결한 품질 코드가 아닙니다. 그렇게 생각해서 미안합니다. 코드는 오류가 유리한 상황을 숨 깁니다.
gshauger

635

JetBrains IntelliJ IDEA , Eclipse 또는 Netbeans 와 같은 Java IDE 또는 findbugs와 같은 도구를 사용하거나 사용하려는 경우 주석을 사용하여이 문제를 해결할 수 있습니다.

기본적으로 @Nullable@NotNull.

다음과 같이 메소드 및 매개 변수에 사용할 수 있습니다.

@NotNull public static String helloWorld() {
    return "Hello World";
}

또는

@Nullable public static String helloWorld() {
    return "Hello World";
}

두 번째 예제는 컴파일되지 않습니다 (IntelliJ IDEA에서).

helloWorld()다른 코드에서 첫 번째 함수 를 사용하는 경우 :

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

이제 IntelliJ IDEA 컴파일러는 helloWorld()함수 가을 반환하지 않기 때문에 검사가 쓸모 없다고 알려줍니다 null.

매개 변수 사용

void someMethod(@NotNull someParameter) { }

다음과 같이 쓰면 :

someMethod(null);

컴파일되지 않습니다.

사용하는 마지막 예 @Nullable

@Nullable iWantToDestroyEverything() { return null; }

이렇게

iWantToDestroyEverything().something();

그리고 당신은 이것이 일어나지 않을 것이라고 확신 할 수 있습니다. :)

컴파일러가 평소보다 더 많은 것을 확인하고 계약을 강화하도록하는 좋은 방법입니다. 불행히도 모든 컴파일러에서 지원하지는 않습니다.

IntelliJ IDEA 10.5 이상에서는 다른 @Nullable @NotNull구현에 대한 지원을 추가했습니다 .

보다 유연하고 구성 가능한 @ Nullable / @ NotNull 주석 블로그 게시물을 참조하십시오 .


120
@NotNull, @Nullable다른 nullness 주석과의 일부인 JSR 305 . FindBugs 와 같은 도구의 잠재적 인 문제를 탐지하는 데 사용할 수도 있습니다 .
Jacek S

32
나는 @NotNull& @Nullable인터페이스가 패키지에 있다는 것이 이상하게 짜증 난다 com.sun.istack.internal. (com.sun을 독점 API 사용에 대한 경고와 연결한다고 생각합니다.)
Jonik

20
Jacek S와 마찬가지로 JSR303이라고 생각되는 방식으로 JSR의 일부라고 말했다.
자바 카 베이비

10
커스텀 컴파일러를 사용하는 것이이 문제에 대한 실용적인 해결책이라고 생각하지 않습니다.
Shivan Dragon

64
주석에 대한 좋은 일 @NotNull하고 @Nullable있는 소스 코드를 이해하지 않는 시스템에 의해 작성 될 때 그들은 정중하게 저하 될 것입니다. 따라서 실제로 코드가 이식 가능하지 않다는 주장은 유효하지 않을 수 있습니다. 이러한 주석을 지원하고 이해하는 시스템을 사용하는 경우보다 엄격한 오류 검사의 추가 이점을 얻을 수 있습니다. 그렇지 않으면 코드가 적지 만 코드는 여전히 어쨌든 런타임에 이러한 주석이 적용되지 않았으므로 실행중인 프로그램의 품질은 동일합니다. 게다가 모든 컴파일러는 커스텀 ;-)
amn

321

널값이 허용되지 않는 경우

메소드가 외부에서 호출되면 다음과 같이 시작하십시오.

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

그런 다음 나머지 방법에서 이것이 objectnull이 아님을 알 수 있습니다.

내부 메소드 (API의 일부가 아닌) 인 경우 null이 될 수 없다는 것을 문서화하십시오.

예:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

그러나 메소드가 값을 전달하고 다음 메소드가 값을 전달하면 문제가 발생할 수 있습니다. 이 경우 위와 같이 인수를 확인하십시오.

널이 허용되는 경우

이것은 실제로 달려 있습니다. 내가 종종 이런 식으로 뭔가를 발견하면 :

if (object == null) {
  // something
} else {
  // something else
}

그래서 저는 분기하고 완전히 다른 두 가지 일을합니다. 데이터에 따라 두 가지 다른 작업을 수행해야하기 때문에 추악한 코드 스 니펫이 없습니다. 예를 들어 입력 작업을 수행해야합니까, 아니면 올바른 기본값을 계산해야합니까?


관용구 " if (object != null && ..." 를 사용하는 것은 실제로 드 rare니다 .

일반적으로 관용구를 사용하는 위치의 예를 보여 주면 예를 제공하는 것이 더 쉬울 수 있습니다.


94
IllegalArgumentException을 발생시키는 요점은 무엇입니까? NullPointerException이 더 명확하다고 생각하며 null 확인을 직접 수행하지 않으면 throw됩니다. 나는 assert를 사용하거나 전혀 사용하지 않을 것이다.
Axel

23
null 이외의 다른 모든 값이 허용되는 것은 아닙니다. IllegalArgumentException, OutOfRageException 등을 가질 수 있습니다. 때로는 이것이 의미가 있습니다. 다른 경우에는 값을 추가하지 않는 많은 예외 클래스를 만들면 IllegalArgumentException을 사용합니다. null 입력에 대한 예외와 다른 모든 것에 대한 예외를 갖는 것은 의미가 없습니다.
myplacedk

7
예, 실패-빠른 원칙에 동의하지만 위의 예에서는 값이 전달되지 않지만 메소드가 호출되는 오브젝트입니다. 따라서 동일하게 빠르게 실패하며, 동시에 어쨌든 발생하는 예외를 throw하기 위해 null 검사를 추가해도 디버깅이 더 쉬워 보이지는 않습니다.
Axel

8
보안 구멍? JDK는 이와 같은 코드로 가득합니다. 사용자가 스택 추적을 보지 못하게하려면 비활성화하십시오. 아무도 그 행동이 문서화되지 않았다는 것을 암시하지 않았습니다. MySQL은 C로 작성되어 null 포인터를 역 참조하는 것이 정의되지 않은 동작으로 예외를 던지는 것과 같습니다.
fgb

8
throw new IllegalArgumentException("object==null")
Thorbjørn Ravn Andersen

238

와우, 나는 우리가 57 가지 다른 방법을 추천 할 때 또 다른 대답을 추가하는 것을 거의 싫어 NullObject pattern하지만,이 질문에 관심이있는 사람들은 Java 7에 대해 "null-safe" 를 추가하라는 제안이 있다는 것을 알고 싶어 할 것입니다 handling " — if-nqual-null 논리를위한 간소화 된 구문.

Alex Miller가 제공 한 예는 다음과 같습니다.

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

?.만 디 레퍼런스가 널이 아닌 경우 좌측 식별자 수단은 그렇지 같은 식의 나머지를 평가 null. Java Posse 회원 Dick Wall 및 Devoxx유권자 와 같은 일부 사람들 은이 제안을 정말 좋아하지만 실제로 null는 센티넬 가치로 더 많이 사용하도록 장려한다는 반대 입장도 있습니다.


업데이트 : Java 7의 null 안전 연산자에 대한 공식 제안Project Coin 에 제출되었습니다 . 구문은 위의 예제와 약간 다르지만 같은 개념입니다.


업데이트 : null 안전 연산자 제안서가 Project Coin으로 제안되지 않았습니다. 따라서 Java 7에서는이 구문이 표시되지 않습니다.


20
나는 이것이 틀렸다고 생각한다. 주어진 변수가 항상 널이 아닌 것으로 지정하는 방법이 있어야합니다.
Thorbjørn Ravn Andersen 님이

5
업데이트 : 제안은 Java7을 만들지 않습니다. blogs.sun.com/darcy/entry/project_coin_final_five를 참조하십시오 .
Boris Terzic

9
흥미로운 아이디어이지만 구문의 선택은 터무니 없습니다. 모든 조인트에 물음표로 가득 찬 코드베이스를 원하지 않습니다.
Rob

7
이 연산자는 Groovy에 존재 하므로이를 사용하려는 사람들은 여전히 ​​옵션으로 사용합니다.
Muhd

8
이것이 내가 본 가장 독창적 인 아이디어입니다. C 구문의 모든 현명한 언어에 추가해야합니다. 차라리 여러 줄을 스크롤하거나 하루 종일 "가드 조항"을 피하는 것보다는 모든 곳에서 "물음표"를 사용하고 싶습니다.
Victor

196

정의되지 않은 값이 허용되지 않는 경우 :

잠재적 인 null 역 참조에 대해 경고하도록 IDE를 구성 할 수 있습니다. 예를 들어 Eclipse에서 환경 설정> Java> 컴파일러> 오류 / 경고 / 널 분석을 참조하십시오 .

정의되지 않은 값이 허용되는 경우 :

정의되지 않은 값이 적합한 새 API를 정의 하려면 옵션 패턴을 사용하십시오 (기능적 언어에 익숙 할 수 있음). 다음과 같은 장점이 있습니다.

  • 입력 또는 출력이 존재하는지 여부는 API에 명시 적으로 명시되어 있습니다.
  • 컴파일러는 "정의되지 않은"경우를 처리하도록합니다.
  • 옵션은 모나드 이므로 자세한 null 검사가 필요하지 않습니다. map / foreach / getOrElse 또는 유사한 결합기를 사용하여 값을 안전하게 사용하십시오 (example) .

Java 8에는 내장 Optional클래스가 있습니다 (권장). 이전 버전, 라이브러리 대안이 있습니다 예를 들어, 구아바Optional또는 FunctionalJavaOption. 그러나 많은 기능적 스타일 패턴과 마찬가지로 Java에서 옵션 (8 개)을 사용하면 상당히 약간의 상용구가 생겨서 Scala 또는 Xtend와 같이 덜 장황한 JVM 언어를 사용하여 줄일 수 있습니다.

null을 반환 할 수 있는 API를 처리해야하는 경우 Java에서 많은 작업을 수행 할 수 없습니다. Xtend와 Groovy는 Elvis 연산자 ?:null 안전 역 참조 연산자 ?. 를 가지고 있지만 null 참조의 경우 null을 반환하므로 null의 적절한 처리를 "정의"합니다.


22
실제로 옵션 패턴은 훌륭합니다. 일부 Java 동등 물이 존재합니다. 구아바에는 옵션이라는 제한된 버전이 포함되어 있으며 대부분의 기능적인 부분은 제외됩니다. Haskell에서는이 패턴을 Maybe라고합니다.
벤 하디

9
선택적 클래스는 Java 8에서 사용할 수 있습니다.
Pierre Henry

1
... 그리고 아직 map이나 flatMap이 없습니다 : download.java.net/jdk8/docs/api/java/util/Optional.html
thSoft

3
선택적 패턴은 아무것도 해결하지 못합니다. 잠재적으로 null 객체 하나 대신 이제 두 개가 있습니다.
Boann

1
@Boann,주의해서 사용하면 모든 NPE 문제를 해결할 수 있습니다. 그렇지 않다면 "사용법"문제가있는 것 같습니다.
Louis F.

189

이 상황에서만-

equals 메서드를 호출하기 전에 변수가 null인지 확인하지 않음 (아래 문자열 비교 예) :

if ( foo.equals("bar") ) {
 // ...
}

A의 발생합니다 NullPointerException경우 foo존재하지 않습니다.

다음 String과 같이 비교하면 피할 수 있습니다 .

if ( "bar".equals(foo) ) {
 // ...
}

42
나는 그 상황에서만 동의합니다. 나는 이것을 다음의 불필요한 수준으로 가져간 if if (null! = myVar) ... 프로그래머가 못 생겼고 목적을 제공하지 않는 프로그래머를 견딜 수 없습니다!
Alex Worden

20
이것은 일반적인 모범 사례 중 가장 많이 사용되는 특정 예입니다. 알고있는 경우 항상 수행하십시오 <object that you know that is not null>.equals(<object that might be null>);. equals계약을 알고 있고 해당 메소드가 null매개 변수 를 처리 할 수있는 경우 이외의 다른 메소드에 대해서도 작동합니다 .
Stef

9
이것이 실제로 의미 가있는 Yoda Conditions에서 본 첫 번째 예입니다.
Erin Drummond

6
이유로 인해 NullPointerException이 발생합니다. 객체는 null이되어서는 안되는 null이기 때문에 발생합니다. 이 문제를 해결하는 것이 아니라이를 수정하는 것이 프로그래머의 작업입니다.
Oliver Watkins

1
Try-Catch-DoDo 아무것도 문제를 숨기지 않습니다. 이것은 언어에서 설탕이 빠져 나가는 것을 해결하는 데 유효한 방법입니다.
echox

167

Java 8에는 java.util.Optional문제의 일부를 해결 하는 새로운 클래스가 있습니다. 적어도 코드의 가독성을 향상시키고 퍼블릭 API의 경우 API 계약을 클라이언트 개발자에게 명확하게 만들 수 있다고 말할 수 있습니다.

그들은 다음과 같이 작동합니다.

주어진 유형 ( Fruit)에 대한 선택적 객체 는 메소드의 리턴 유형으로 작성됩니다. 비어 있거나 Fruit개체를 포함 할 수 있습니다 .

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

이제 주어진 Fruit 인스턴스에 대한 Fruit( fruits) 목록을 검색하는이 코드를 살펴보십시오 .

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

map()연산자를 사용하여 선택적 객체에서 계산을 수행하거나 값을 추출 할 수 있습니다 . orElse()결 측값을 대체 할 수 있습니다.

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

물론, null / empty 값에 대한 점검은 여전히 ​​필요하지만 최소한 개발자는 값이 비어있을 수 있음을 인식하고 점검 잊어 버릴 위험이 제한적입니다.

Optional반환 값이 비어있을 때마다 사용하고 처음부터 일반 객체를 반환 할 수없는 경우 null(컨벤션)를 사용하여 처음부터 빌드 한 API에서 클라이언트 코드는 간단한 객체 반환 값에 대한 null 검사를 포기할 수 있습니다 ...

물론 Optional메소드 인수로 사용될 수도 있습니다. 경우에 따라 5 또는 10 개의 오버로드 메소드보다 선택적 인수를 나타내는 더 좋은 방법 일 수도 있습니다.

OptionalorElse기본값을 사용 ifPresent하고 람다 식과 함께 작동 하는 것과 같은 다른 편리한 방법을 제공합니다 .

이 기사 (이 답변을 작성하는 주요 소스)를 읽고 NullPointerException문제가 있는 (및 일반적으로 null 포인터) 문제와 (일부) 솔루션에 Optional대해 잘 설명 된 Java Optional Objects 를 읽으십시오 .


11
Google의 구아바에는 Java 6 이상에 대한 선택적 암시가 있습니다.
Bradley Gottfried

13
그것은이다 매우 에만 ifPresent와 옵션 ()를 사용하는 않는 것을 강조하는 것이 중요 하지 일반 널 (null) 검사보다 많은 가치를 추가 할 수 있습니다. 핵심 가치는 그것이 map / flapMap의 함수 체인에서 사용될 수있는 모나드라는 것인데, 다른 곳에서 언급 한 Groovy의 Elvis 연산자와 유사한 결과를 얻습니다. 그러나이 사용법이 없어도 orElse / orElseThrow 구문도 매우 유용합니다.
Cornel Masson


4
사람들이 if(optional.isPresent()){ optional.get(); }대신 이런 행동을하는 이유optional.ifPresent(o -> { ...})
Satyendra Kumar

1
따라서 API 계약 힌트 이외의 것은 체인 방식을 끝없이 즐기는 기능 프로그래머에게 음식을 제공하는 것입니다.
호감

125

확인하는 객체의 종류에 따라 apache commons langapache commons collections 와 같은 Apache Commons에서 일부 클래스를 사용할 수 있습니다.

예:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

또는 (확인해야 할 사항에 따라) :

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

StringUtils 클래스는 많은 것 중 하나 일뿐입니다. null 안전 조작을 수행하는 커먼즈에는 꽤 좋은 클래스가 있습니다.

다음은 아파치 라이브러리 (commons-lang-2.4.jar)를 포함 할 때 JAVA에서 null vallidation을 사용하는 방법의 예입니다.

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

Spring을 사용하는 경우 Spring은 패키지에 동일한 기능을 가지고 있습니다. library (spring-2.4.6.jar)

spring (org.springframework.util.Assert)에서이 정적 클래스를 사용하는 방법에 대한 예제

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

5
또한 Apache Commons의보다 일반적인 버전을 사용하여 메소드를 시작할 때 찾은 매개 변수를 확인하는 데 매우 유용합니다. Validate.notNull (객체, "객체는 null이 아니어야합니다"); commons.apache.org/lang/apidocs/org/apache/commons/lang/...
monojohnny

@monojohnny는 Validate를 사용하여 Assert 문을? Assert가 JVM에서 활성화 / 비활성화 될 수 있기 때문에 프로덕션 환경에서 사용하지 않는 것이 좋습니다.
Kurapika

그렇게 생각하지 마십시오 - 나는 유효성 검사가 실패하면 그냥 RuntimeException을 던졌습니다 생각
monojohnny

96
  • 객체가 null이 아니거나 버그 인 경우에는 assert를 사용하십시오.
  • 메소드가 널 매개 변수를 허용하지 않으면 javadoc에서 말하고 어설 션을 사용하십시오.

객체가 null 일 수있는 경우를 처리하려는 경우에만 객체! = null을 확인해야합니다 ...

null / notnull 매개 변수를 지원하기 위해 Java7에 새 주석을 추가하라는 제안이 있습니다. http://tech.puredanger.com/java7/#jsr308



91

나는 "실패"코드의 팬입니다. 스스로에게 물어보십시오-매개 변수가 null 인 경우 유용한 것을하고 있습니까? 이 경우 코드가 무엇을 해야하는지에 대한 명확한 대답이 없다면 ... 즉, 처음에는 null이 아니어야하며 무시하고 NullPointerException이 발생하도록하십시오. 호출 코드는 IllegalArgumentException과 마찬가지로 NPE와 거의 일치하지만, 개발자가 코드에서 예기치 않은 다른 우발 상황을 실행하려고 시도하는 것보다 NPE가 발생하면 무엇이 잘못되었는지 디버그하고 이해하는 것이 더 쉽습니다. 논리-결국 응용 프로그램이 중단됩니다.


2
어설 션을 사용하는 것이 좋습니다. 즉 Contract.notNull (abc, "abc는 null이 아니어야합니다. xyz 동안로드에 실패 했습니까?"); -이것은 if (abc! = null) {throw new RuntimeException ...}을 수행하는 것보다 더 간단한 방법입니다.
ianpojman

76

Google 컬렉션 프레임 워크는 null 검사를 수행 할 수있는 훌륭하고 우아한 방법을 제공합니다.

라이브러리 클래스에는 다음과 같은 메소드가 있습니다.

static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}

그리고 사용법은 (와 import static)입니다.

...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...

또는 귀하의 예에서 :

checkNotNull(someobject).doCalc();

71
음, 차이점은 무엇입니까? p.getAge ()는 적은 오버 헤드와 더 명확한 스택 추적으로 동일한 NPE를 발생시킵니다. 내가 무엇을 놓치고 있습니까?
mysomic 2016 년

15
프로그래머가 의도 한 예외 (유지 관리자가 문제점을 식별 할 수 있도록 충분한 정보와 함께)임을 명확하게 나타내므로 예제에서 IllegalArgumentException ( "e == null")을 처리하는 것이 좋습니다. 그 다음 분명이 의도하지 않은 것을 나타냅니다 (일반적으로 식별하는 곳 열심히 일)로 NullPointerExceptions를은 JVM에 예약해야한다
Thorbjørn Ravn 안데르센을

6
이것은 이제 구글 구아바의 일부입니다.
Steven Benitez

32
나에게 너무 공학적인 냄새가 난다. JVM이 NPE를 던지도록 하고이 정크로 코드를 어지럽히 지 마십시오.
Alex Worden

5
나는 그것을 좋아하고 인수에 대한 명시 적 검사를 통해 대부분의 메소드와 생성자를 엽니 다. 오류가있는 경우 항상 처음 몇 줄에서 메소드가 실패하고 다음과 같은 것을 찾지 않고 문제를 일으키는 참조를 알고 있습니다.getThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi");
Cory Kendall

75

때로는 매개 변수를 조작하여 대칭 조작을 정의하는 메소드가 있습니다.

a.f(b); <-> b.f(a);

b가 null이 될 수 없다는 것을 알고 있다면 그냥 바꿀 수 있습니다. 그것은 동등한에 가장 유용합니다 : 대신에 foo.equals("bar");더 잘 할 "bar".equals(foo);.


2
그러나 그런 다음 equalsnull을 올바르게 처리 한다고 가정해야합니다 (어떤 방법이든 가능). 실제로이 모든 일은 책임을 다른 사람 (또는 다른 방법)에게 전달하는 것입니다.
Supericy

4
@Supericy 기본적으로 그렇습니다 (그러나 equals어떤 방법이든) null어쨌든 확인해야 합니다. 또는 그렇지 않다고 명시 적으로 진술하십시오.
Angelo Fuchs

74

Null 객체 패턴 (용도 사용)이 아니라 null 객체가 버그 인 상황을 고려할 수 있습니다.

예외가 발생하면 스택 추적을 검사하고 버그를 해결하십시오.


17
문제는 일반적으로 NullPointerException이 WHICH 변수가 널임을 나타내지 않으므로 컨텍스트를 느슨하게하고 라인에 여러 개의 "."조작이있을 수 있다는 것입니다. "if (foo == null) throw new RuntimeException ("foo == null ")"을 사용하면 WHAT가 잘못되었음을 명시 적으로 명시 할 수있어 스택 추적 문제를 해결해야하는 사람들에게 더 많은 가치를 제공합니다.
Thorbjørn Ravn Andersen

1
Andersen을 사용하면 Java 예외 시스템이 처리중인 변수의 이름을 포함 할 수 있기를 원하므로 NullPointerExceptions는 예외가 발생한 줄뿐만 아니라 변수 이름도 나타낼 수 있습니다. 명확하지 않은 소프트웨어에서는 제대로 작동합니다.
fwielstra

나는 아직 작업을 진행하게 관리하지 않은,하지만 이것은 정확하게 문제를 해결하기위한 것입니다.
MatrixFrog

무엇이 문제입니까? 충분한 컨텍스트를 사용할 수있는 적절한 레벨에서 NPE를 잡고 컨텍스트 정보를 덤프하고 예외를 다시 발생시키기 만하면 Java에서는 매우 쉽습니다.
user1050755

3
메소드 콜 체인에 대해 설교하는 교수가있었습니다. 그의 이론은 2 가지 방법보다 긴 콜 체인에주의해야한다는 것이 었습니다. 이것이 어려운 규칙인지는 모르겠지만 NPE 스택 추적과 관련된 대부분의 문제를 확실히 제거합니다.
RustyTheBoyRobot

74

널은 '문제'가 아닙니다. 완벽한 모델링 도구 세트 의 필수 요소입니다 . 소프트웨어는 세계의 복잡성을 모델링하는 것을 목표로하며, 그 부담은 없습니다. Null은 Java 등에서 '데이터 없음'또는 '알 수 없음'나타냅니다 . 따라서 이러한 목적으로 null을 사용하는 것이 좋습니다. 나는 'Null object'패턴을 선호하지 않습니다. 나는 ' 후견인을 지키는 사람 '문제를 일으킨다 .
내 여자 친구의 이름이 무엇인지 물어 보면 여자 친구가 없다고 말할 수 있습니다. Java 언어에서는 null을 반환합니다. 대안은 할 수없는 (또는하지 말아야 할) 문제를 나타 내기 위해 의미있는 예외를 던지는 것입니다

  1. '알 수없는 질문'은 '알 수없는 답변'을 제시하십시오. (비즈니스 관점에서 올바른 경우 null 안전) 사용하기 전에 메서드 내에서 null 인수를 한 번 확인하면 여러 호출자가 호출하기 전에 인수를 확인하지 않아도됩니다.

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }

    이전에는 사진 라이브러리에서 존재하지 않는 여자 친구의 사진을 가져 오지 않기 위해 정상적인 논리 흐름으로 이어집니다.

    getPhotoOfThePerson(me.getGirlfriend())

    그리고 앞으로 나올 새로운 Java API에 적합합니다.

    getPhotoByName(me.getGirlfriend()?.getName())

    어떤 사람에게는 DB에 저장된 사진을 찾지 못하는 것이 오히려 '정상적인 업무 흐름'이지만 다른 경우에는 아래와 같이 쌍을 사용했습니다.

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);

    그리고 입력하는 것을 싫어하지 말고 <alt> + <shift> + <j>(Eclipse에서 javadoc을 생성하십시오) 공용 API를 위해 세 가지 단어를 추가로 작성하십시오. 이것은 문서를 읽지 않는 사람들을 위해 충분할 것입니다.

    /**
     * @return photo or null
     */

    또는

    /**
     * @return photo, never null
     */
  2. 이것은 다소 이론적 인 경우이며 대부분의 경우 java null safe API를 선호해야하지만 (10 년 후에 출시 될 경우)의 NullPointerException하위 클래스입니다 Exception. 따라서 Throwable합리적인 응용 프로그램이 잡을 수있는 조건을 나타내는 형식입니다 ( javadoc )! 예외의 첫 번째 가장 큰 장점을 사용하고 오류 처리 코드를 '일반적인'코드와 분리하려면 ( Java 제작자에 따라 ) 나에게도 적합 NullPointerException합니다.

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }

    질문이 생길 수 있습니다.

    Q. getPhotoDataSource()null을 반환 하면 어떻게 됩니까?
    A. 비즈니스 로직에 달려 있습니다. 사진 앨범을 찾지 못하면 사진이 표시되지 않습니다. appContext가 초기화되지 않으면 어떻게됩니까? 이 방법의 비즈니스 로직은이를 반영합니다. 동일한 로직이보다 엄격해야하는 경우 예외를 발생시키는 것은 비즈니스 로직의 일부이며 널에 대한 명시 적 점검을 사용해야합니다 (사례 3). 더 나은 여기에 새로운 자바 널 안전 API에 맞는 초기화 할 수 선택적으로 무엇을 의미하고 무엇을 의미하는 것은 아니다 지정 르파 프로그래머 오류가 발생하는 경우에있을 수 있습니다.

    Q. 중복 코드가 실행될 수 있고 불필요한 리소스가 확보 될 수 있습니다.
    A. getPhotoByName()데이터베이스 연결을 열고 PreparedStatement마지막으로 개인 이름을 작성하여 SQL 매개 변수로 사용 하려는 경우 발생할 수 있습니다 . 알 수없는 질문에 대한 접근 방식 은 알 수없는 대답 (사례 1)을 제공합니다. 리소스를 가져 오기 전에 메서드는 매개 변수를 확인하고 필요한 경우 '알 수없는'결과를 반환해야합니다.

    Q.이 방법은 시험 폐쇄 개방으로 인해 성능이 저하됩니다.
    A. 소프트웨어는 먼저 이해하고 수정하기 쉬워야합니다. 이 후에야 성능에 대해 생각할 수 있으며 필요한 경우에만 가능합니다! 그리고 필요한 곳! ( 출처 ) 및 기타 여러 가지).

    추신. 이 방법은 "일반적인"코드 원칙 과 다른 오류 처리 코드 가 어떤 장소에서 사용하기에 합리적이기 때문에 사용하기에 합리적입니다. 다음 예를 고려하십시오.

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }

    PPS. 공감대가 빠르거나 문서를 빨리 ​​읽지 못하는 사람들을 위해 나는 인생에서 널 포인터 예외 (NPE)를 포착 한 적이 없다고 말하고 싶습니다. 그러나 NPE가의 하위 클래스이기 때문에 이러한 가능성은 의도적으로 Java 작성자가 설계 했습니다 Exception. 때 우리는 자바의 역사에서 전례가 ThreadDeath입니다 Error실제로 응용 프로그램 오류 때문에하지 만이 잡힐 수 없습니다되었다 이유만으로! 얼마나 NPE는이 수에 맞는 Error보다 ThreadDeath! 그러나 그렇지 않습니다.

  3. 비즈니스 로직이 의미하는 경우에만 '데이터 없음'을 확인하십시오.

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }

    appContext 또는 dataSource가 처리되지 않은 런타임으로 초기화되지 않은 경우 NullPointerException은 현재 스레드를 종료하고 Thread.defaultUncaughtExceptionHandler에 의해 처리됩니다 (사용자가 선호하는 로거 또는 기타 알림 메커니즘 을 정의하고 사용하기 위해). 설정하지 않으면 ThreadGroup # uncaughtException 은 스택 추적을 시스템 err에 인쇄합니다. 처리되지 않은 각 예외 (사실 응용 프로그램 오류)에 대해 응용 프로그램 오류 로그를 모니터링하고 Jira 문제를 열어야합니다. 프로그래머는 초기화 작업에서 버그를 수정해야합니다.


6
잡기 NullPointerException와 리턴 null은 디버그하기가 끔찍합니다. 어쨌든 나중에 NPE로 끝나고 원래 null이 무엇인지 파악하기가 실제로 어렵습니다.
artbristol

23
명성이 있다면 공감할 것입니다. 널 (null)은 필요하지 않을뿐만 아니라 타입 시스템의 구멍입니다. 트리가 List 유형의 값이 아니므로 트리를 목록에 지정하는 것은 유형 오류입니다. 같은 논리에 따라 null을 할당하는 것은 형식 오류 여야합니다. null은 Object 형식의 값이 아니거나 해당 문제에 유용한 형식이 아니기 때문입니다. null을 발명 한 사람조차도 그것을 "십억 달러의 실수"로 간주합니다. "T 유형의 값일 수도 있고 아무것도 아닌 값"이라는 개념은 고유 한 유형이며, 예를 들어 Maybe <T> 또는 Optional <T>와 같이 표시되어야합니다.
Doval

2
"Maybe <T> 또는 Optional <T>"기준으로 여전히 코드를 작성해야 if (maybeNull.hasValue()) {...}하므로 차이점은 if (maybeNull != null)) {...}무엇입니까?
Mykhaylo Adamovych

1
"NullPointerException을 잡아서 null을 반환하는 것은 디버깅하기가 끔찍합니다. 나중에 NPE로 끝날 수 있으며 원래 null이 무엇인지 파악하기가 정말 어렵습니다." 나는 완전히 동의한다! 이러한 경우 비즈니스 로직이 데이터를 제자리에 암시하거나 새로운 Java의 null 안전 연산자를 사용하는 경우 12 개의 'if'문을 작성하거나 NPE를 발생시켜야합니다. 그러나 정확한 단계가 나에게 null을주는 것에 신경 쓰지 않는 경우가 있습니다. 예를 들어 데이터가 누락 될 것으로 예상 될 때 화면에 표시되기 직전에 사용자에 대한 일부 값을 계산합니다.
Mykhaylo Adamovych

3
@MykhayloAdamovych :의 장점은 Maybe<T>또는 Optional<T>당신이 경우에없는 T하지만이 null 일 수 없습니다 경우, 널 (null) 일 수 있습니다. 명시 적으로 "이 값 null 일 수 있습니다-주의해서 사용하십시오 "를 의미하는 유형 있고 이러한 유형을 일관되게 사용하고 반환하는 경우 T코드에서 평범한 구식이 보일 때마다 null 이 아니라고 가정 할 수 있습니다. (코스, 이것은 컴파일러가 시행 할 수 있다면 훨씬 더 유용 할 것입니다.)
cHao

71

Java 7에는 메소드 가있는 새로운 java.util.Objects유틸리티 클래스가 requireNonNull()있습니다. 이 모든 것은 NullPointerException인수가 null 인 경우 a를 던지지 만 코드를 약간 정리합니다. 예:

Objects.requireNonNull(someObject);
someObject.doCalc();

이 메소드는 생성자에서 할당 직전 에 확인하는 데 가장 유용하며 , 각 메소드를 사용하면 세 줄의 코드를 저장할 수 있습니다.

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

된다

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}

9
실제로, 귀하의 예는 코드 팽창을 구성합니다. 첫 번째 줄은 NPE가 두 번째 줄에 던져지기 때문에 불필요합니다. ;-)
user1050755 1

1
진실. 더 좋은 예는 두 번째 줄이 인 경우 doCalc(someObject)입니다.
스튜어트 마크 5

다릅니다. doCalc ()의 저자라면 가능하면 해당 메소드의 본문에 확인을 넣는 것이 좋습니다. 그런 다음 null을 확인할 필요가없는 someObject.someMethod ()를 호출합니다. :-)
user1050755

글쎄, 당신이 저자 doCalc()가 아니며 null이 주어질 때 NPE를 즉시 던지지 않으면 null을 확인하고 NPE를 스스로 던져야합니다. 그게 다야 Objects.requireNonNull().
Stuart Marks

7
그것은 단지 코드 팽창이 아닙니다. 부작용을 일으키거나 시간 / 공간을 사용하는 방법을 통해 중간보다 앞쪽에서 확인하는 것이 좋습니다.
Rob Grant

51

궁극적으로이 문제를 완전히 해결하는 유일한 방법은 다른 프로그래밍 언어를 사용하는 것입니다.

  • Objective-C에서는에 메소드를 호출하는 것과 동등한 작업을 수행 할 수 있으며 nil아무 일도 일어나지 않습니다. 따라서 대부분의 null 검사가 불필요하지만 오류를 진단하기가 훨씬 어려워 질 수 있습니다.
  • Java 파생 언어 인 Nice 에는 모든 유형의 널 버전과 널이 아닌 버전의 두 가지 버전이 있습니다. 널이 아닌 유형에서만 메소드를 호출 할 수 있습니다. 널 (null)을 명시 적으로 검사하여 잠재적으로 널 (null) 유형을 널이 아닌 유형으로 변환 할 수 있습니다. 따라서 null 검사가 필요한 위치와 필요하지 않은 위치를 훨씬 쉽게 알 수 있습니다.

와우 ... 가장 정답과 답은 하향 조정됩니다. 정의는 어디에 있습니까? Java에서 null은 항상 유효한 값입니다. 그것은 모든 것의 대상입니다-Null은 모두입니다 (물론 우리는 여기서 기본 요소를 무시하지만 아이디어를 얻습니다). 개인적으로 Nice가 취한 접근 방식을 선호하지만 nullable 유형에서 메소드를 호출하고 NPE를 확인 된 예외로 승격시킬 수 있습니다. 기존의 모든 코드를 깨뜨릴 수 있지만 컴파일러 스위치를 통해 수행해야합니다. (
CurtainDog

4
나는 Nice에 익숙하지 않지만 Kotlin은 동일한 아이디어를 구현하고 nullable 및 null이 아닌 유형을 언어 유형 시스템에 빌드합니다. Optionals 또는 null Object 패턴보다 훨씬 간결합니다.
mtsahakis

38

실제로 Java의 일반적인 "문제".

먼저, 이것에 대한 나의 생각 :

NULL이 유효하지 않은 곳에서 NULL이 전달 될 때 무언가를 "먹는"것은 좋지 않다고 생각합니다. 어떤 종류의 오류로 메소드를 종료하지 않으면 메소드에 아무런 문제가 없다는 것을 의미합니다. 그런 다음이 경우 null을 반환하고 수신 방법에서 다시 null을 확인하고 끝나지 않으며 "if! = null"등으로 끝납니다.

따라서 IMHO, null은 추가 실행을 방지하는 중대한 오류 여야합니다 (즉, null이 유효한 값이 아닌 경우).

이 문제를 해결하는 방법은 다음과 같습니다.

먼저이 규칙을 따릅니다.

  1. 모든 공개 메소드 / API는 항상 null에 대한 인수를 확인합니다
  2. 모든 개인 메소드는 제어 된 메소드이기 때문에 null을 검사하지 않습니다 (위에서 처리되지 않은 경우 널 포인터 예외로 죽게하십시오)
  3. 널을 점검하지 않는 유일한 다른 메소드는 유틸리티 메소드입니다. 그것들은 공개적이지만 어떤 이유로 호출하면 어떤 매개 변수를 전달하는지 알 수 있습니다. 이것은 물을 공급하지 않고 주전자에서 물을 끓이려고하는 것과 같습니다 ...

마지막으로 코드에서 공개 메소드의 첫 번째 행은 다음과 같습니다.

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

addParam ()은 self를 반환하므로 확인할 매개 변수를 더 추가 할 수 있습니다.

매개 변수 중 하나가 null 인 경우 메서드 validate()가 검사 ValidationException됩니다 (체크 또는 체크되지 않은 디자인 / 맛 문제이지만 my ValidationException가 선택되었습니다).

void validate() throws ValidationException;

예를 들어 "plans"가 null 인 경우 메시지에 다음 텍스트가 포함됩니다.

" 매개 변수 [plans]에 대해 잘못된 인수 값 null이 발생했습니다. "

보시다시피 addParam () 메소드의 두 번째 값 (문자열)은 사용자 메시지에 필요합니다. 반향이 있어도 전달 된 변수 이름을 쉽게 감지 할 수 없기 때문입니다.

그리고 네, 우리는이 줄을 넘어 서면 더 이상 널 (null) 값을 겪지 않을 것입니다.

이런 식으로 코드는 깨끗하고 유지 관리가 쉽고 읽을 수 있습니다.


3
물론. 오류가 발생하고 충돌하는 응용 프로그램은 작동하지 않을 때 의심의 여지가 없으므로 품질이 높습니다. 오류를 최대한 삼키는 응용 프로그램은 정상적으로 성능이 저하되지만 일반적으로 알기 어렵고 수정되지 않는 방식으로 작동하지 않습니다. 그리고 문제가 발견되면 디버깅하기가 훨씬 어렵습니다.
Paul Jackson

35

이 질문을하면 오류 처리 전략에 관심이있을 수 있습니다. 팀의 설계자는 오류를 처리하는 방법을 결정해야합니다. 이를 수행하는 몇 가지 방법이 있습니다.

  1. 예외가 파급되도록 허용- '메인 루프'또는 다른 관리 루틴에서 예외를 포착하십시오.

    • 오류 상태를 확인하고 적절히 처리하십시오

Aspect Aspected Programming도 살펴보아야한다 if( o == null ) handleNull(). 바이트 코드 에 삽입하는 깔끔한 방법이있다 .


35

사용 외에도 다음을 사용할 assert수 있습니다.

if (someobject == null) {
    // Handle null here then move on.
}

이것은 다음보다 약간 낫습니다.

if (someobject != null) {
    .....
    .....



    .....
}

2
왜 그런가요? 제발 수비를 느끼지 마십시오. Java에 대해 더 배우고 싶습니다 :)
Matthias Meid

9
@Mudu 일반적으로 if 문의 표현은 "부정적인"문보다는 "긍정적 인"문인 것을 선호합니다. 그래서 내가 본다면 if (!something) { x(); } else { y(); }리팩토링하는 경향이 있습니다 if (something) { y(); } else { x(); }(그러나 != null더 긍정적 인 옵션 이라고 주장 할 수 는 있지만 ...). 그러나 더 중요한 것은 코드의 중요한 부분이 안에 싸여 있지 않으며 {}대부분의 메소드에 대해 들여 쓰기 수준이 한 단계 낮아진 것입니다. 그것이 빠른 코드인지는 모르겠지만 Java의 추론입니다.
MatrixFrog 2016 년

1
이것이 내가하는 일이기도합니다. 내 의견에 코드를 깨끗하게 유지하십시오.
Koray Tugay

34

null을 사용하지 마십시오. 허용하지 마십시오.

내 클래스에서 대부분의 필드와 로컬 변수에는 null이 아닌 기본값이 있으며 코드의 어느 곳에 나 계약 문 (항상 주장하는 내용)을 추가 하여이 내용이 적용되는지 확인합니다 (간결하고 표현력이 뛰어 나기 때문에) NPE로 올라 와서 줄 번호 등을 해결해야합니다.)

이 연습을 채택한 후에는 문제가 스스로 해결되는 것처럼 보였습니다. 우연히 개발 과정에서 훨씬 일찍 일을 포착하고 약점을 발견했습니다. 더 중요한 것은 서로 다른 모듈의 문제를 캡슐화하는 데 도움이되며 다른 모듈은 서로 '신뢰'할 수 있으며 더 이상 쓰레기를 버리지 않습니다. if = null else구조 와 코드 !

이것은 방어적인 프로그래밍이며 장기적으로 훨씬 깨끗한 코드를 생성합니다. 예를 들어 엄격한 표준을 적용하여 데이터를 항상 삭제하면 문제가 해결됩니다.

class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}

계약은 생산 과정에서도 항상 실행되는 미니 유닛 테스트와 같으며 일이 실패 할 때 임의의 NPE가 아닌 이유를 알아야합니다.


왜 이것이 다운 보트 될까요? 내 경험상, 이것은 다른 접근 방식보다 훨씬 우수합니다. 이유를 알고 싶어합니다
ianpojman

이 접근법은 코드 널 검사를 어디에나 지정하여 문제를 해결하는 대신 null과 관련된 문제를 방지한다는 데 동의합니다.
ChrisBlom

7
이 방법의 문제점은 이름을 설정하지 않으면 "<unknown>"값이 설정 값처럼 작동한다는 것입니다. 이제 이름이 설정되지 않았는지 (알 수 없음) 확인해야한다고 가정하고 특수 값 "<unknown>"에 대해 문자열 비교를 수행해야합니다.
Steve Kuo

1
좋은 지적 스티브. 내가 자주하는 일은 그 값을 상수로하는 것입니다. 예를 들어 public static final String UNSET = "__ unset"... private String field = UNSET ... then private boolean isSet () {return UNSET.equals (field); }
ianpojman

IMHO, 이것은 선택 사항 (계약)을 직접 구현 한 Null 개체 패턴의 구현입니다. 지속성 클래스 수업에서 어떻게 작동합니까? 이 경우 해당되는 것으로 보이지 않습니다.
Kurapika

31

Google의 매우 유용한 핵심 라이브러리 인 Guava에는 null을 피할 수있는 훌륭하고 유용한 API가 있습니다. UsingAndAvoidingNullExplained가 매우 유용 하다는 것을 알았 습니다.

위키에 설명 된대로 :

Optional<T>널 입력 가능 T 참조를 널이 아닌 값으로 바꾸는 방법입니다. 선택 사항은 널이 아닌 T 참조 (이 경우 참조가 "현재"라고 함)를 포함하거나 아무것도 포함하지 않을 수 있습니다 (이 경우 참조가 "없음"이라고 함). "널을 포함한다"고 말한 적이 없습니다.

용법:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

@CodyGuldner 맞다, 코디. 더 많은 컨텍스트를 제공하기 위해 링크에서 관련 견적을 제공했습니다.
Murat Derya Özen

25

이것은 모든 Java 개발자에게 매우 일반적인 문제입니다. 따라서 Java 8에서는 복잡한 코드없이 이러한 문제를 해결하기위한 공식적인 지원이 있습니다.

Java 8이 도입되었습니다 java.util.Optional<T>. 널이 아닌 값을 보유하거나 보유하지 않을 수있는 컨테이너입니다. Java 8에서는 일부 경우에 값이 null 일 수있는 객체를보다 안전하게 처리 할 수 ​​있습니다. 그것은 HaskellScala 의 아이디어에서 영감을 얻었습니다 .

요컨대, Optional 클래스에는 값이 있거나없는 경우를 명시 적으로 처리하는 메서드가 포함되어 있습니다. 그러나 null 참조와 비교할 때의 이점은 Optional <T> 클래스를 사용하면 값이없는 경우에 대해 생각해야한다는 것입니다. 결과적으로 의도하지 않은 널 포인터 예외를 방지 할 수 있습니다.

위의 예에서는 가정에서 사용 가능한 여러 기기에 핸들을 반환하는 가정 서비스 팩토리가 있습니다. 그러나 이러한 서비스는 사용 가능하거나 작동하지 않을 수 있습니다. NullPointerException이 발생할 수 있음을 의미합니다. if서비스를 사용하기 전에 null 조건 을 추가하는 대신 Optional <Service>로 감싸도록하겠습니다.

랩핑 옵션 <T>

공장에서 서비스에 대한 참조를 얻는 방법을 고려해 봅시다. 서비스 참조를 리턴하는 대신 선택 사항으로 랩하십시오. 반환 된 서비스가 사용 가능하거나 작동하지 않을 수 있음을 API 사용자에게 알려 방어 적으로 사용

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

보시다시피 Optional.ofNullable()참조를 래핑하는 쉬운 방법을 제공합니다. Optional.empty()& 의 선택 사항을 얻는 다른 방법이 있습니다 Optional.of(). 하나는 null을 재조정하는 대신 빈 객체를 반환하고 다른 하나는 null을 허용하지 않는 객체를 각각 래핑하기위한 것입니다.

그렇다면 NULL 점검을 피하는 것이 정확히 어떻게 도움이됩니까?

참조 객체를 래핑 한 후 Optional은 NPE없이 래핑 된 참조에서 메서드를 호출하는 많은 유용한 메서드를 제공합니다.

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Optional.ifPresent는 널이 아닌 값인 경우 참조로 지정된 이용자를 호출합니다. 그렇지 않으면 아무것도하지 않습니다.

@FunctionalInterface
public interface Consumer<T>

단일 입력 인수를 허용하고 결과를 반환하지 않는 작업을 나타냅니다. 대부분의 다른 기능 인터페이스와 달리 소비자는 부작용을 통해 작동해야합니다. 너무 깨끗하고 이해하기 쉽습니다. 위의 코드 예제 HomeService.switchOn(Service)에서 Optional holding reference가 null이 아닌 경우 호출됩니다.

우리는 null 조건을 확인하기 위해 삼항 연산자를 자주 사용하고 대체 값 또는 기본값을 반환합니다. 선택 사항은 null을 확인하지 않고 동일한 조건을 처리하는 다른 방법을 제공합니다. Optional.orElse (defaultObj)는 Optional에 null 값이 있으면 defaultObj를 반환합니다. 샘플 코드에서 이것을 사용하자 :

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

이제 HomeServices.get ()은 같은 일을하지만 더 나은 방법으로 수행합니다. 서비스가 이미 초기화되지 않았는지 확인합니다. 그렇다면 같은 것을 반환하거나 새로운 새 서비스를 만듭니다. 선택적인 <T> .ElEl (T)는 기본값을 반환하는 데 도움이됩니다.

마지막으로 NPE와 null check-free 코드가 있습니다.

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

완전한 게시물은 NPE뿐만 아니라 Null 무 검사 코드입니다. .


위의 코드에는 null 검사 if(homeServices != null) {가 있습니다 homeServices.ifPresent(h -> //action).
KrishPrabakar

23

나는 Nat Pryce의 기사를 좋아한다. 링크는 다음과 같습니다.

이 기사에는 Java Maybe Type에 대한 Git 저장소에 대한 링크가 있습니다.하지만 흥미로운 것으로 확인하지만 검사 코드 팽창을 줄일 수 있다고 생각하지는 않습니다. 인터넷에서 약간의 연구를 한 후에 는 신중한 디자인으로 ! = null code bloat를 줄일 수 있다고 생각 합니다.


마이클 페더스 (Michael Feathers)는 당신이 언급 한 것과 같은 접근 방식에 대해 짧고 흥미로운 글을 썼습니다. manuelp.newsblur.com/site/424
ivan.aguirre

21

나는 시도 NullObjectPattern했지만 나에게 항상 최선의 방법은 아닙니다. "조치 없음"이 적절하지 않은 경우가 있습니다.

NullPointerExceptionA는 런타임 예외 수단 그것의 개발자는 오류 및 충분한 경험과 당신이 정확히 어디에 오류가 알 수 있다고는.

이제 대답에 :

모든 속성과 접근자를 가능한 한 비공개로 만들거나 클라이언트에 노출시키지 마십시오. 물론 생성자에 인수 값을 가질 수 있지만 범위를 줄이면 클라이언트 클래스가 유효하지 않은 값을 전달하지 못하게됩니다. 값을 수정해야하는 경우 언제든지 새을 만들 수 있습니다 object. 생성자에서 한 번만 값을 확인하고 나머지 메소드에서는 값이 널이 아님을 거의 확신 할 수 있습니다.

물론 경험이이 제안을 이해하고 적용하는 더 좋은 방법입니다.

바이트!


19

아마도 Java 8 이상에 대한 가장 좋은 대안은 Optional클래스 를 사용하는 것 입니다.

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

이것은 가능한 null 값의 긴 체인에 특히 유용합니다. 예:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

null에 예외를 던지는 방법에 대한 예 :

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7에는 Objects.requireNonNullnull이 아닌 것을 검사해야 할 때 편리한 방법이 도입되었습니다 . 예:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();

17

더 일반적으로 대답하겠습니다.

우리는 일반적으로 메소드가 예상하지 않은 방식으로 매개 변수를 가져올 때이 문제에 직면합니다 (잘못된 메소드 호출은 프로그래머의 결함입니다). 예를 들어, 객체를 얻을 것으로 예상하고 대신 null을 얻습니다. 적어도 하나의 문자가있는 문자열을 얻을 것으로 예상되는 대신 빈 문자열을 얻습니다 ...

따라서 다음과 같은 차이점이 없습니다.

if(object == null){
   //you called my method badly!

}

또는

if(str.length() == 0){
   //you called my method badly again!
}

그들은 우리가 다른 기능을 수행하기 전에 유효한 매개 변수를 받았는지 확인하려고합니다.

다른 답변에서 언급했듯이 위의 문제를 피하기 위해 계약 패턴 별로 디자인을 따를 수 있습니다 . http://en.wikipedia.org/wiki/Design_by_contract를 참조하십시오 .

이 패턴을 Java로 구현하려면 javax.annotation.NotNull 과 같은 핵심 Java 어노테이션을 사용하거나 Hibernate Validator 와 같은보다 정교한 라이브러리를 사용할 수 있습니다 .

단지 샘플 :

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

이제 입력 매개 변수를 확인할 필요없이 분석법의 핵심 기능을 안전하게 개발할 수 있으며 예기치 않은 매개 변수로부터 분석법을 보호합니다.

한 단계 더 나아가 응용 프로그램에서 유효한 pojo 만 작성할 수 있습니다. (최대 절전 모드 검사기 사이트의 샘플)

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}

javax정의상 "핵심 Java"가 아닙니다 .
Tomas

17

모든 상황에서 null 객체 사용을 제안하는 답변을 무시합니다. 이 패턴은 계약을 위반하고 문제를 해결하는 대신 깊고 깊이 파 묻는 문제를 부각시킬 수 있습니다. 부적절하게 사용하면 향후 유지 보수가 필요한 보일러 플레이트 코드가 생성됩니다.

실제로 메소드에서 리턴 된 것이 널이 될 수 있고 호출 코드가이를 결정해야하는 경우, 상태를 보장하는 이전 호출이 있어야합니다.

또한 null 객체 패턴은주의없이 사용하면 메모리가 부족하다는 점을 명심하십시오. 이를 위해 NullObject의 인스턴스는 소유자간에 공유되어야하며 각 인스턴스의 고유 인스턴스가 아니어야합니다.

또한 유형이 스칼라가 아닌 수학적 엔티티와 같은 기본 유형 표현 인 벡터, 행렬, 복소수 및 POD (Plain Old Data) 객체 인 경우이 패턴을 사용하지 않는 것이 좋습니다. Java 내장 유형의 형태로. 후자의 경우 임의의 결과로 getter 메소드를 호출하게됩니다. 예를 들어 NullPerson.getName () 메소드는 무엇을 반환해야합니까?

터무니없는 결과를 피하기 위해 그러한 경우를 고려해 볼 가치가 있습니다.


"hasBackground ()"가있는 솔루션에는 한 가지 단점이 있습니다. 스레드 안전하지 않습니다. 하나 대신 두 개의 메소드를 호출해야하는 경우 멀티 스레드 환경에서 전체 시퀀스를 동기화해야합니다.
pkalinow 12

@pkalinow이 솔루션에는 단점이 있음을 지적하기 위해 유념 한 예제를 만들었습니다. 코드가 멀티 스레드 응용 프로그램에서 실행되도록되어 있지 않으면 단점이 없습니다. 스레드 안전하지 않은 코드의 90 %를 아마 넣을 수 있습니다. 우리는 여기서이 코드 측면에 대해 말하지 않고 디자인 패턴에 대해 말하고 있습니다. 그리고 멀티 스레딩은 그 자체로 주제입니다.
luke1985

물론 단일 스레드 응용 프로그램에서는 문제가되지 않습니다. 때로는 문제가 있기 때문에 그 의견을주었습니다.
pkalinow 2019

@pkalinow이 주제를 자세히 살펴보면 Null 개체 디자인 패턴이 멀티 스레딩 문제를 해결하지 못한다는 것을 알 수 있습니다. 따라서 관련이 없습니다. 그리고 솔직히 말해서,이 패턴이 잘 어울리는 곳을 찾았습니다. 그래서 원래의 대답은 약간 틀 렸습니다.
luke1985

16
  1. 변수를 null로 초기화하지 마십시오.
  2. (1)을 사용할 수 없으면 모든 컬렉션과 배열을 빈 컬렉션 / 배열로 초기화하십시오.

자신의 코드 에서이 작업을 수행하면! = null 검사를 피할 수 있습니다.

대부분의 경우 null 검사는 컬렉션이나 배열에 대한 루프를 보호하는 것처럼 보이므로 빈 채로 초기화하면 null 검사가 필요하지 않습니다.

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}

이것에는 작은 오버 헤드가 있지만 더 깨끗한 코드와 적은 NullPointerExceptions에 가치가 있습니다.



3
+1 동의합니다. 초기화 된 객체의 절반을 반환해서는 안됩니다. Jaxb 관련 코드 및 Bean 코드는 이에 대한 notiroius입니다. 나쁜 습관입니다. 모든 컬렉션은 초기화되어야하며 모든 객체는 null 참조가없는 (이상적으로) 존재해야합니다. 컬렉션이있는 개체를 고려하십시오. 개체가 null이 아니고 컬렉션이 null이 아니고 컬렉션에 null 개체가 포함되어 있지 않은지 확인하는 것은 비합리적이고 어리 석습니다.
ggb667

15

이것은 대부분의 개발자에게 발생한 가장 일반적인 오류입니다.

이를 처리 할 수있는 방법이 많이 있습니다.

접근법 1 :

org.apache.commons.lang.Validate //using apache framework

notNull (객체 객체, 문자열 메시지)

접근법 2 :

if(someObject!=null){ // simply checking against null
}

접근법 3 :

@isNull @Nullable  // using annotation based validation

접근법 4 :

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}

기원 후. 4. 매우 유용하지는 않습니다. 포인터가 널인지 확인하면 메소드를 호출하려고합니다. null에서 메소드를 호출하면 NullPointerException과 동일한 동작이 발생합니다.
pkalinow

11
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}

2
이 방법의 문제점은 @tltester가 null 인 경우 기본값을 지정하려고한다고 생각합니다.
소여

1
Apache commons-lang에는 그러한 방법이 있습니다 ObjectUtils.defaultIfNull(). : 일반적인 일이 ObjectUtils.firstNonNull()분해 전략을 구현하는 데 사용할 수 있습니다 :firstNonNull(bestChoice, secondBest, thirdBest, fallBack);
ivant
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.