MockitoのargThatとScala 3 (original) (raw)
一見かなり謎なタイミングで NullPointerException
で混乱したので、かるくメモ。
以下、再現コード。
scalaVersion := "3.5.1-RC1"
libraryDependencies += "org.mockito" % "mockito-subclass" % "5.12.0"
package example
object Main { def main(args: Array[String]): Unit = { try { println("型引数を明示しなかった場合") val b = org.mockito.ArgumentMatchers.argThat((a: String) => a.isEmpty) println(b) println("成功") } catch { case e: Throwable => println("失敗 " + e) }
println("")
try {
println("型引数を明示した場合")
val b = org.mockito.ArgumentMatchers.argThat[String](a => a.isEmpty)
println(b)
println("成功")
} catch {
case e: Throwable =>
println("失敗 " + e)
}
} }
Scala 3だと
ArgumentMatchers.argThat((a: String) => a.isEmpty)
と
ArgumentMatchers.argThat[String](a => a.isEmpty)
で、違う挙動になります。後者の方が想定した挙動になる・・・はず?
Scala 3.5.1-RC1での実行結果例。
型引数を明示しなかった場合 失敗 java.lang.NullPointerException
型引数を明示した場合 null 成功
Scala 2.13.14だとどちらで書いても同じっぽい。
Scala 3では変数にアクセスするタイミング?でエラーという謎な挙動・・・。
とりあえず再現コードは作れたが -Xprint:all
したり javap
して眺めてみたけど、詳細に正確には現象を理解出来てないので、理解したらもっと詳細な解説を書くかもしれません。
TODO: mockito関係なく単体で再現コード作れるのか?も、あとで試す・・・
MockitoもJavaも関係なく書けますね、こう。Scalaで書くと asInstanceOf
必要だが
package example
object Main { def foo[A](f: A => Boolean): A = null.asInstanceOf[A]
def main(args: Array[String]): Unit = { val y1 = foo[String](x => x.isEmpty) println(y1)
val y2 = foo((x: String) => x.isEmpty)
println(y2)
} }
Scala 3だと型引数がどこで使われるか?によって、型推論結果が変わるようです。 ある意味ではScala 3の方が頭がいいような気がしつつ、Mockitoのようにリフレクション使うようなライブラリとは相性が良くないですね。
Welcome to Scala 3.5.1-RC1 (21.0.4, Java OpenJDK 64-Bit Server VM). Type in expressions for evaluation. Or try :help.
scala> def f1[A](x: A => String): A = ??? def f1[A](x: A => String): A
scala> def f2[A](x: A => String): A => Int = ??? def f2[A](x: A => String): A => Int
scala> def f3[A](x: A => String): String => A = ??? def f3[A](x: A => String): String => A
scala> def a1 = f1((s: String) => s) def a1: Nothing
scala> def a2 = f2((s: String) => s) def a2: String => Int
scala> def a3 = f3((s: String) => s) def a3: String => Nothing