Scalaの演算子はメソッド、ではなぜ==がぬるぽにならないのか
Scalaの本やblogを読んでいるとScalaの演算子はメソッドである、という説明が出てきます。
今日はその話です。
+演算子をメソッド呼び出しで
実際にやってみると・・・
scala> val a = 10
a: Int = 10
scala> val b = 20
b: Int = 20
scala> val res1 = a + b
res1: Int = 30
scala> val res2 = a.+(b)
res2: Int = 30
a.+(b)が成功します。
==演算子をメソッド呼び出しで・・・ぬるぽにならない
ここで一つ疑問が生まれます。
「==もメソッド呼び出しであるのならば、==のCaller側がnullだったらぬるぽになるんじゃないの?」です。
scala> val a = null
a: Null = null
scala> val b = 20
b: Int = 20
scala> val res = a.==(b)
<console>:9: warning: comparing values of types Null and Int using `==' will always yield false
val res = a.==(b)
^
res: Boolean = false
(warningは出ていますが、それは一旦置いておいて)NullPointerExceptionが発生していません。Javaならaがnullならぬるぽになりますね。Scalaでもaがnullの状態で他のメソッドを呼び出すとぬるぽになります。
例えば
scala> val nullString: String = null
nullString: String = null
scala> nullString.length
java.lang.NullPointerException
... 33 elided
なぜ==だけ・・・と思ってscalajp/public - Gitterで質問したら@xuwei_kさんが教えてくれました。
どうやらScalaコンパイラの仕様みたいです。nullチェックのコードを自動で仕込むから、だそうです。
逆コンパイルで確認
以下のコードをscalacでコンパイルして、JavaDecompilerで逆コンパイルし、確認してみます。
元コード
object Main extends App {
val a = null
val b = 20
if(a == b) println("equal!!!") else println("not equal...")
}
実行結果
[tsukaby@tsukamac tmp]% scala Main
not equal...
逆コンパイル結果の一部
public final void delayedEndpoint$Main$1()
{
this.a = null;
this.b = 20;
a(); localInteger = BoxesRunTime.boxToInteger(b());
tmp25_16 = null; if (tmp25_16 == null) { tmp25_16; if (localInteger == null) break label44; tmpTernaryOp = tmp25_16; break label55;
}
}
分かりづらいですが、勝手にifによるnullチェックが入っています。
という訳で、ぬるぽにならないことが分かりました。