倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

「?.」演算子についての注意

Groovy の「?.」演算子*1について、ちょっと気になったことがあったのでメモ。

まず、以下のようなクラスがあるとしましょう:

class Foo{
    def getNull(){
        return null
    }
}

getNull() メソッドは常に null を返します。 このメソッドの戻り値に対して「?.」演算子を使って処理を連結します。

++i


まず、「?.」演算子の後に呼び出されるメソッドの引数に「++i」を含める場合。 これは「妙な振る舞いだ」と思う人も、「普通だ」と思う人もいるかと思います。

def foo = new Foo()
int i = 0

foo.getNull()?.someMethod(++i)
assert i == 1

someMethod() メソッドは何処にも定義していないので呼ばれてないはずですが、++i は評価されて、最終的に変数 i の値は1になります。

i++


次は上記のコードで「i++」にした場合。

def foo = new Foo()
int i = 0

foo.getNull()?.someMethod(i++)
assert i == 1

結果は同じで、変数 i の値は1になります。

その他のメソッド


Java では「++i」や「i++」の評価の順序は「そう決まっている」と思うしかないんでしょうけど、Groovy では、これらはメソッド呼び出しになるので、上記と同じようなことがメソッド呼び出しでも起こるかもしれません。 ということで実際にやってみます。

まず、以下のようなクラスを作りましょう:

class Bar{
    def println(){
        println 'Bar'
    }
}

Foo クラスは上のサンプルと同じとします。 このとき、

def foo = new Foo()
def bar = new Bar()

foo.getNull()?.someMethod(bar.println())

とすると、Bar#println() メソッドが実行されて「Bar」という文字列が表示されます。

結局、「?.」演算子の後にあっても、メソッドに渡される引数(の位置にある式)は評価されてしまうようです。 一見ややこしいコードはできるだけ書かないようにするべきなんでしょうけど、最後のメソッド呼び出しの例は、知らず知らずのうちにやってしまいそう。

Groovyイン・アクション

Groovyイン・アクション

*1:この演算子の名前はなんていうんでしょう? 英語では「Safe Navigation Operator」となってますが。 日本語名が特に決まってないなら、「目の上のたんこぶ演算子ってのはどうでしょう?