자바 "?" null 검사 연산자-무엇입니까? (삼항이 아닙니다!)


88

나는 slashdot 이야기에서 링크 된 기사를 읽고 있었고,이 작은 정보를 발견했습니다.

끝없는 포인터 테스트를위한 속기 구문을 제공하여 널 포인터 검사를 더 쉽게 만드는 최신 버전의 Java를 사용하십시오. 각 메서드 호출에 물음표를 추가하기 만하면 null 포인터에 대한 테스트가 자동으로 포함되어 다음과 같이 쥐의 if-then 문을 대체합니다.

    public String getPostcode(Person person) {
      String ans= null;
      if (person != null) {
        Name nm= person.getName();
        if (nm!= null) {
          ans= nm.getPostcode();
        }
      }
      return ans
    } 

이것으로 :

public String getFirstName(Person person) {
      return person?.getName()?.getGivenName();
    } 

나는 인터넷을 샅샅이 뒤졌지만 (좋아, "자바 물음표"에 대한 인터넷 검색에 적어도 15 분을 보냈다) 아무것도 얻지 못했다. 그래서, 내 질문 : 이것에 대한 공식 문서가 있습니까? C #에 유사한 연산자 ( "??"연산자)가 있지만 작업중인 언어에 대한 문서를 얻고 싶습니다. 전에 본적이 없다.

감사!

편집 : 기사 링크 : http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292


3
적어도 기사에 대한 링크를 가질 수 있습니까?
Karl Knechtel 2010

1
그리고 스 니펫의 출처는?
khachik 2010

5
기사가 잘못되었습니다. infoworld.com/print/145292 프로젝트 코인 제출이 제출 된 것 같습니다. 그러나 그것은 선택되지 않았고 (기사에 언급 된 이유 때문에-그런 종류의 일을하고 싶다면 C # 등을 사용하십시오) 확실히 현재 버전의 Java 언어에 포함되어 있지 않습니다.
Tom Hawtin-tackline

4
그것은 C #과 같지 않습니까 ?? 운영자: ?? null을 통합합니다 A ?? B == (A != null) ? A : B. 즉 . 이것은 객체 참조가 null이 아닌 경우 객체의 속성을 평가하는 것처럼 보입니다 A?.B == (A != null) ? A.B : null.
Rup

1
@Erty : 기본적으로 내가 작성하는 코드의 모든 곳에있는 @NotNull 주석의 거대한 사용자로서 NPE가 무엇인지 더 이상 잘 모릅니다 (심하게 desing API를 사용하는 경우 제외). 그러나 나는이 "바로 가기 표기법"이 귀엽고 흥미 롭다고 생각한다. 물론 다음과 같은 기사는 옳습니다. 결국 문제의 근원 인 빠르고 느슨한 프로그래밍으로 인한 null 값의 확산을 제거하지는 않습니다. 'null'은 OOA / OOD 수준에 존재하지 않습니다. 대부분 해결 될 수있는 또 다른 Java 고유 동 기적 넌센스입니다. 나에게 그것은 모든 곳 에서 @NotNull 입니다.
SyntaxT3rr0r 2010

답변:


80

원래 아이디어는 그루비에서 나왔습니다. 프로젝트 코인 : https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC(Elvis 및 기타 Null-Safe 연산자)의 일부로 Java 7 용으로 제안 되었지만 아직 승인되지 않았습니다. .

관련 엘비스 연산자는? 만들기 위해 제안되었다 x ?: y위한 속기 x != null ? x : yx는 복잡한 표현 인 경우 특히 유용합니다.


3
대한 속기 (널 (null)에는 자동 강제 없다) 자바x!=null ? x : y
마이클 Borgwardt

@Michael Borgwardt : 좋은 지적입니다. 저는 그루비 한 의미론을 생각하고있었습니다.
ataylor

50
?엘비스 프레슬리의 시그니처 퀴프입니다. 은 :단지 평소와 같이 눈의 쌍을 나타냅니다. 아마도 ?:-o더 연상이 될 것 입니다 ...
Andrzej Doyle

4
?:0"Not null 또는 0"연산자 여야합니다. 그렇게 만들어.
azz

3
실제로 제안을 Elvis 운영자라고 부르는 것은 실수였습니다. mail.openjdk.java.net/pipermail/coin-dev/2009-July/002089.html의 설명 "Null-safe dereferencing"이 사용하기에 더 좋은 용어입니다.
JustinKSU

63

이 구문은 Java에 존재하지 않으며 내가 아는 향후 버전에 포함될 예정이 아닙니다.


9
왜 반대 투표입니까? 이 대답은 내가 아는 한 100 % 맞습니다. 뭔가 다른 것을 알고 있다면 그렇게 말 해주세요.
ColinD 2010

6
@Webinator : Java 7 또는 8에는 없을 것이며 현재로서는 다른 "예정 버전"도 없습니다. 나는 또한 그것이 오히려 나쁜 관행을 장려하기 때문에 그것이 될 것 같지 않다는 것을 발견했습니다. 또한 "Java에 존재하지 않음"이 "Java에 존재하지 않을 것"과 같지 않기 때문에 "아직"이 필요하다고 생각하지 않습니다.
ColinD 2010

9
@Webinator : 여러 포스터에서 제안이 제출되었지만 거부되었다고 언급했습니다. 따라서 대답은 100 % 정확합니다. 반대표에 대응하기위한 찬성.
JeremyP 2010-12-08

2
@ColinD 나쁜 관행 이 못생긴 코드를 포기하고 사용하기로 결정한 경우입니다 Optionalmap물건. 값이 nullable이면 문제를 숨기지 않습니다. 즉, 때때로 null이 될 것으로 예상되며이를 처리해야합니다. 때로는 기본값이 완벽하게 합리적이고 나쁜 습관이 아닙니다.
M.kazem Akhgary

2
Java는 다소 현대적이기를 거부합니다. 이 언어를 이미 종료하십시오.
Plagon 2020 년


19

"?"부족을 해결하는 한 가지 방법 try-catch의 오버 헤드없이 Java 8을 사용하는 연산자 ( NullPointerException언급 한대로 다른 곳에서 시작된 것을 숨길 수도 있음 )는 Java-8-Stream 스타일의 "파이프"메서드에 대한 클래스를 만드는 것입니다.

public class Pipe<T> {
    private T object;

    private Pipe(T t) {
        object = t;
    }

    public static<T> Pipe<T> of(T t) {
        return new Pipe<>(t);
    }

    public <S> Pipe<S> after(Function<? super T, ? extends S> plumber) {
        return new Pipe<>(object == null ? null : plumber.apply(object));
    }

    public T get() {
        return object;
    }

    public T orElse(T other) {
        return object == null ? other : object;
    }
}

그러면 주어진 예는 다음과 같습니다.

public String getFirstName(Person person) {
    return Pipe.of(person).after(Person::getName).after(Name::getGivenName).get();
}

[편집하다]

더 깊이 생각해 보면 표준 Java 8 클래스를 사용해야 만 실제로 동일한 결과를 얻을 수 있다는 것을 알았습니다.

public String getFirstName(Person person) {
    return Optional.ofNullable(person).map(Person::getName).map(Name::getGivenName).orElse(null);
}

이 경우 매개 변수로 전달하는 "<no first name>"대신 기본값 (예 :)을 선택할 수도 있습니다 .nullorElse


나는 당신의 첫 번째 해결책을 더 좋아합니다. 메서드 내에서 null이 아닌 임의의 개체를 전달할 수 있도록 기능 Pipe을 조정하기 위해 클래스를 어떻게 개선 할까요? orElseorElse
ThanosFisherman

@ThanosFisherman 클래스에 orElse메서드를 추가했습니다 Pipe.
헬더 페레이라


7

그것은 실제로 Groovy의 안전 역 참조 연산자 입니다. 순수한 Java에서는 사용할 수 없으므로 (슬프게도) 게시물이 잘못되었습니다 (또는 Groovy가 "Java의 최신 버전"이라고 주장하는 경우 약간 오해의 소지가 있음).


2
그래서 기사가 잘못되었습니다-구문은 네이티브 자바에 존재하지 않습니다. 흠.
Erty Seidohl 2010

하지만 링크가 끊어졌습니다
bvdb

6

Java에는 정확한 구문이 없지만 JDK-8부터는 다양한 메소드를 사용할 수있는 Optional API 가 있습니다. 따라서 null 조건부 연산자를 사용하는 C # 버전 :

return person?.getName()?.getGivenName(); 

선택적 API를 사용하여 Java에서 다음과 같이 작성할 수 있습니다 .

 return Optional.ofNullable(person)
                .map(e -> e.getName())
                .map(e -> e.getGivenName())
                .orElse(null);

person, getName또는 중 하나 getGivenName가 null이면 null이 반환됩니다.


2

Java 8 람다를 사용하여 거의 예쁘게이 문제를 해결하는 util 메소드를 정의 할 수 있습니다.

이것은 H-MAN 솔루션 의 변형 이지만 여러 인수가있는 오버로드 된 메서드를 사용하여 NullPointerException.

이 솔루션이 멋지다고 생각하더라도 헬더 페레이라의 세컨드 솔루션을 선호한다고 생각 합니다.

void example() {
    Entry entry = new Entry();
    // This is the same as H-MANs solution 
    Person person = getNullsafe(entry, e -> e.getPerson());    
    // Get object in several steps
    String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());
    // Call void methods
    doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());        
}

/** Return result of call to f1 with o1 if it is non-null, otherwise return null. */
public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {
    if (o1 != null) return f1.apply(o1);
    return null; 
}

public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {
    return getNullsafe(getNullsafe(o0, f1), f2);
}

public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {
    return getNullsafe(getNullsafe(o0, f1, f2), f3);
}


/** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */
public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {
    if (o1 != null) f1.accept(o1);
}

public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {
    doNullsafe(getNullsafe(o0, f1), f2);
}

public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {
    doNullsafe(getNullsafe(o0, f1, f2), f3);
}


class Entry {
    Person getPerson() { return null; }
}

class Person {
    Name getName() { return null; }
}

class Name {
    void nameIt() {}
    String getGivenName() { return null; }
}

1

이것이 효과가 있을지 모르겠습니다. 예를 들어, 사람 참조가 null 인 경우 런타임은이를 무엇으로 대체합니까? 새로운 사람? 이 경우에 예상 할 수있는 기본 초기화가 Person에게 필요합니다. null 참조 예외를 피할 수 있지만 이러한 유형의 설정을 계획하지 않은 경우 여전히 예측할 수없는 동작이 발생합니다.

? C #의 연산자는 "coalesce"연산자로 가장 잘 불릴 수 있습니다. 여러 표현식을 연결할 수 있으며 null이 아닌 첫 번째 표현식을 반환합니다. 불행히도 Java에는 없습니다. 가장 좋은 방법은 삼항 연산자를 사용하여 null 검사를 수행하고 체인의 멤버가 null 인 경우 전체 표현식에 대한 대안을 평가하는 것입니다.

return person == null ? "" 
    : person.getName() == null ? "" 
        : person.getName().getGivenName();

try-catch를 사용할 수도 있습니다.

try
{
   return person.getName().getGivenName();
}
catch(NullReferenceException)
{
   return "";
}

1
"런타임은 그것을 무엇으로 대체할까요?" ... 질문을 읽는 것이 도움이 될 수 있습니다 :-P null로 대체됩니다. 일반적으로 아이디어는 person? .getName이 사람이 null이면 null로 평가되고 그렇지 않으면 person.getName으로 평가됩니다. 따라서 모든 예제에서 ""를 null로 바꾸는 것과 거의 같습니다.
subsub

1
NullReferenceException이 던져 getName()졌거나 getGivenName()모든 발생에 대해 단순히 빈 문자열을 반환하는지 여부를 알 수 없습니다.
Jimmy T.

1
Java에서는 NullPointerException입니다.
Tuupertunut 2017 년

C #에서는 person?.getName()?.getGivenName() ?? ""첫 번째 예제와 동일합니다. 단 getGivenName(), null을 반환하면 여전히""
Austin_Anderson

0

Java 8에서 null-safe 호출이 있습니다.

public void someMethod() {
    String userName = nullIfAbsent(new Order(), t -> t.getAccount().getUser()
        .getName());
}

static <T, R> R nullIfAbsent(T t, Function<T, R> funct) {
    try {
        return funct.apply(t);
    } catch (NullPointerException e) {
        return null;
    }
}

나는 이것을 시도해야 할 것이다. SI는이 "선택적"사업 전체에 대해 심각한 의구심을 가지고 있습니다. 불쾌한 해킹처럼 보입니다.
ggb667 2014-06-05

3
Elvis Operator 제안의 전체 목적은이 작업을 한 줄로하는 것이 었습니다. 이 접근 방식은 위의 "if (! = null) 접근 방식보다 낫지 않습니다. 사실 솔직하지 않기 때문에 더 나쁘다고 생각합니다. 또한 오버 헤드로 인한 오류를 던지고 잡는 것을 피해야합니다.
JustinKSU

@Darron이 다른 답변에서 말했듯이 여기에도 동일하게 적용됩니다. "이 스타일의 문제는 NullPointerException이 예상 한 위치에서 발생하지 않았을 수 있다는 것입니다. 따라서 실제 버그를 숨길 수 있습니다."
Helder Pereira 2015 년

0

누군가 이전 자바 버전에 대한 대안을 찾고 있다면 내가 작성한 다음을 시도해 볼 수 있습니다.

/**
 * Strong typed Lambda to return NULL or DEFAULT VALUES instead of runtime errors. 
 * if you override the defaultValue method, if the execution result was null it will be used in place
 * 
 * 
 * Sample:
 * 
 * It won't throw a NullPointerException but null.
 * <pre>
 * {@code
 *  new RuntimeExceptionHandlerLambda<String> () {
 *      @Override
 *      public String evaluate() {
 *          String x = null;
 *          return x.trim();
 *      }  
 *  }.get();
 * }
 * <pre>
 * 
 * 
 * @author Robson_Farias
 *
 */

public abstract class RuntimeExceptionHandlerLambda<T> {

    private T result;

    private RuntimeException exception;

    public abstract T evaluate();

    public RuntimeException getException() {
        return exception;
    }

    public boolean hasException() {
        return exception != null;
    }

    public T defaultValue() {
        return result;
    }

    public T get() {
        try {
            result = evaluate();
        } catch (RuntimeException runtimeException) {
            exception = runtimeException;
        }
        return result == null ? defaultValue() : result;
    }

}

0

제공 한 코드를 테스트 할 수 있으며 구문 오류가 발생하므로 Java에서는 지원되지 않습니다. Groovy는이를 지원하며 Java 7 용으로 제안되었지만 포함되지 않았습니다.

그러나 Java 8에서 제공하는 옵션을 사용할 수 있습니다. 이는 유사한 행에서 무언가를 달성하는 데 도움이 될 수 있습니다. https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

선택 사항에 대한 예제 코드


0

Android는 설치된 OS가 24보다 크지 않으면 Lambda 함수를 지원하지 않으므로 리플렉션을 사용해야합니다.

// Example using doIt function with sample classes
public void Test() {
    testEntry(new Entry(null));
    testEntry(new Entry(new Person(new Name("Bob"))));
}

static void testEntry(Entry entry) {
    doIt(doIt(doIt(entry,  "getPerson"), "getName"), "getName");
}

// Helper to safely execute function 
public static <T,R> R doIt(T obj, String methodName) {
    try {
       if (obj != null) 
           return (R)obj.getClass().getDeclaredMethod(methodName).invoke(obj);
    } catch (Exception ignore) {
    }
    return null;
}
// Sample test classes
    static class Entry {
        Person person;
        Entry(Person person) { this.person = person; }
        Person getPerson() { return person; }
    }

    static class Person {
        Name name;
        Person(Name name) { this.name = name; }
        Name getName() { return name; }
    }

    static class Name {
        String name;
        Name(String name) { this.name = name; }
        String getName() {
            System.out.print(" Name:" + name + " ");
            return name;
        }
    }
}

-4

이것이 성능 문제가 아니라면 다음과 같이 작성할 수 있습니다.

public String getFirstName(Person person) {
  try {
     return person.getName().getGivenName();
  } catch (NullPointerException ignored) {
     return null;
  }
} 

8
이 스타일의 문제점은 NullPointerException이 예상 한 위치에서 발생하지 않았을 수 있다는 것입니다. 따라서 실제 버그를 숨길 수 있습니다.
Darron

2
@Darron, NPE를 던질 수있는 게터의 예를 들어 주실 수 있고 어떻게 다르게 처리하고 싶습니까?
Peter Lawrey 2010

2
별도의 변수로 실행하고 정상적으로 if로 테스트합니다. 이 연산자 뒤에있는 아이디어는 그 추악함을 제거하는 것입니다. Darron이 옳습니다. 솔루션은 던지고 싶은 예외를 숨기고 버릴 수 있습니다. 예를 들어, getName()버리고 싶지 않은 예외를 내부적으로 던진 경우 .
Mike Miller

2
예외는 아니지만 NullPointerException이어야합니다. 실제 응용 프로그램에서 어떻게 발생할 수 있는지 설명하기 시작하지 않은 상황으로부터 자신을 보호하려고합니다.
피터 Lawrey

1
실제 응용 프로그램에서는 PersonDB 또는 힙이 아닌 메모리에 액세스하는 프록시가 될 수 있으며 어딘가에 버그가있을 수 있습니다.별로 현실적이지는 않지만 Peter, 위와 같은 코드를 작성해 본 적이 없을 것입니다. .
maaartinus 2014
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.