Java「?」nullをチェックするための演算子-それは何ですか?(三項ではありません!)


88

私はスラッシュドットの話からリンクされた記事を読んでいて、この小さな一口に出くわしました:

最新バージョンのJavaを使用してください。これは、エンドレスポインターテスト用の簡略構文を提供することにより、nullポインターチェックを容易にすることを目的としています。各メソッド呼び出しに疑問符を追加するだけで、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();
    } 

私はインターネットを調べましたが(わかりました、「javaクエスチョンマーク」のバリエーションをグーグルで検索するのに少なくとも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言語には含まれていません。
トムホーティン-タックライン2010

4
それはC#と同じではありませんか?演算子:?? ヌルを合体させA ?? B == (A != null) ? A : Bます。これは、オブジェクト参照がnullでない場合、つまり、オブジェクトのプロパティを評価するように見えますA?.B == (A != null) ? A.B : null
Rup 2010

1
@Erty:基本的に私が書いたコードのいたるところにある@NotNullアノテーションの巨大なユーザーとして、私はNPEが何であるかをもうよく知りません(ひどく設計されたAPIを使用する場合を除く)。それでも、この「ショートカット表記」はかわいくて面白いと思います。もちろん、この記事は次のように述べています。結局のところ、問題の根本を排除するわけではありません。高速で緩いプログラミングによるnull値の急増です。「null」はOOA / OODレベルでは存在しません。これは、ほとんど回避できるもう1つのJava固有のナンセンスです。私にとって、それはどこでも@NotNullです。
SyntaxT3rr0r 2010

回答:


80

元のアイデアはグルーヴィーから来ています。プロジェクトコインの一部としてJava7に提案されました:https//wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC(Elvis and Other Null-Safe Operators)が、まだ受け入れられていません。

関連するエルビス演算子?:は、のx ?: y省略形を作成するために提案されましたx != null ? x : y。特に、xが複素式の場合に役立ちます。


3
Java(nullへの自動強制がない場合)の省略形x!=null ? x : y
Michael Borgwardt

@Michael Borgwardt:良い点ですが、私はグルーヴィーなセマンティクスについて考えていました。
ataylor 2010

50
?エルビスプレスリーのシグネチャークイッフです。:ただいつものように目のペアを表します。多分?:-o...もっと刺激的である
アンジェイ・ドイル

4
?:0「nullでも0でもない」演算子である必要があります。そうしてください。
azz 2013

3
提案をエルビス演算子と呼ぶのは実際には間違いでした。mail.openjdk.java.net/pipermail/coin-dev/2009-July/002089.htmlでの説明 「ヌルセーフ逆参照」は、使用するのに適した用語です。
JustinKSU

63

この構文はJavaには存在せず、私が知っている今後のバージョンのいずれにも含まれる予定はありません。


9
なぜ反対票を投じるのですか?私の知る限り、この答えは100%正しいです...何か違うことを知っているなら、そう言ってください。
ColinD 2010

6
@Webinator:Java 7または8には含まれず、現時点では他の「今後のバージョン」はありません。それはかなり悪い習慣を助長するので、私はそれがうまくいく可能性も低いと思います。また、「Javaに存在しない」と「Javaに存在しない」は同じではないので、「まだ」は必要ないと思います。
ColinD 2010

9
@Webinator:いくつかの投稿者が、提案が提出されたが拒否されたとコメントしています。したがって、答えは100%正確です。反対票を打ち消すための賛成票。
JeremyP 2010

2
@ColinDの悪い習慣は、この醜いコードをあきらめて、使用することにしたときです。Optionalmapのもの。値がnull可能である場合、問題を隠しているわけではありません。つまり、nullになることがあると予想され、それを処理する必要があります。デフォルト値が完全に合理的である場合もあり、それは悪い習慣ではありません。
M.kazemAkhgary18年

2
Javaは、やや現代的であることを拒否しているだけです。すでにこの言語を終了するだけです。
プラゴン


19

「?」の欠如を回避する1つの方法 try-catchのオーバーヘッドなしでJava8を使用する演算子(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();
}

[編集]

さらに考えてみると、標準のJava8クラスを使用した場合にのみ同じことを実現できることが実際に可能であることがわかりました。

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 2017年

@ThanosFishermanorElseメソッドをPipeクラスに追加しました。
ヘルダーペレイラ2017


7

これは実際にはGroovyの安全な間接参照演算子です。純粋なJavaでは(悲しいことに)それを使用できないため、投稿は単に間違っています(または、Groovyが「Javaの最新バージョン」であると主張している場合は、少し誤解を招く可能性があります)。


2
そのため、記事は間違っていました-構文はネイティブJavaには存在しません。うーん。
Erty Seidohl 2010

しかし、リンクが壊れています
bvdb 2016年

6

Javaには正確な構文はありませんが、JDK-8の時点で、さまざまなメソッドを自由に使用できるオプションのAPIがあります。したがって、null条件演算子を使用したC#バージョン:

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

オプションのAPIを使用してJavaで次のように記述できます

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

いずれかの場合persongetNameまたはgetGivenNamenullの場合、nullが返されています。


2

Java8ラムダを使用してこれをほぼきれいな方法で解決するutilメソッドを定義することが可能です。

これはH-MANsソリューションのバリエーションですが、キャッチする代わりに複数のステップを処理するために複数の引数を持つオーバーロードされたメソッドを使用しますNullPointerException

このソリューションはちょっとクールだと思いますが、utilメソッドを必要としないので、HelderPereiraの2番目のソリューションの方が好きだと思います。

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の場合、ランタイムはそれを何に置き換えますか?新しい人?そのためには、この場合に予想されるデフォルトの初期化が必要になります。null参照の例外を回避することはできますが、これらのタイプのセットアップを計画していなかった場合でも、予測できない動作が発生します。

?? C#の演算子は、「合体」演算子と呼ばれるのが最適です。複数の式を連鎖させることができ、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が、personがnullの場合はnullと評価され、そうでない場合はperson.getNameと評価されるようです。したがって、すべての例で「」をnullに置き換えるのとほとんど同じです。
subsub 2013

1
NullReferenceExceptionがスローされる可能性もあります。getName()またはgetGivenName()、すべてのオカレンスに対して単に空の文字列を返すかどうかがわかりません。
ジミーT.

1
JavaではNullPointerExceptionです。
tuupertunut 2017年

C#ではperson?.getName()?.getGivenName() ?? ""、これは最初の例と同等ですが、getGivenName()nullを返した場合でも、次のようになります""
Austin_Anderson 2017

0

これで、Java8でのnullセーフな呼び出しができました。

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年

3
エルビス演算子の提案の全体的な目的は、これを1行で行うことでした。このアプローチは、上記の「if(!= null)のアプローチよりも良いではありません、それは単純ではないとして、私はそれが悪化している主張するだろう実際にはさらにあなたが原因のオーバーヘッドにエラーを投げるとキャッチすることは避けてください。。。
JustinKSU

@Darronが別の回答で述べたように、同じことがここでも当てはまります。「このスタイルの問題は、NullPointerExceptionが期待した場所から来ていない可能性があることです。したがって、実際のバグが隠される可能性があります。」
ヘルダーペレイラ

0

誰かが古いJavaバージョンの代替を探しているなら、私が書いたこれを試すことができます:

/**
 * 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

インストールされているOSが24以上でない限り、Androidは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が予期した場所から発生していない可能性があることです。したがって、実際のバグを隠す可能性があります。
ダロン2010

2
@ Darron、NPEをスローする可能性のある、作成したゲッターの例と、それを別の方法で処理する方法を教えてください。
Peter Lawrey 2010

2
それを別の変数に実行し、通常どおりifを使用してテストします。この演算子の背後にある考え方は、その醜さを排除することです。ダロンは正しいです、あなたの解決策はあなたが投げたい例外を隠して捨てるかもしれません。破棄しgetName()たくない例外を内部でスローした場合など。
マイクミラー

2
例外ではありませんが、NullPointerExceptionである必要があります。実際のアプリケーションでどのように発生するかを説明し始めていない状況から身を守ろうとしています。
Peter Lawrey 2010

1
実際のアプリケーションでPersonは、DBまたはヒープ以外のメモリにアクセスするプロキシであり、どこかにバグがある可能性があります...あまり現実的ではありませんが、ピーター、上記のようなコードを書いたことがないに違いありません。 。
maaartinus 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.