なんの記事?
同一クラス内に同一名称のメソッドが、
可変長メソッドと固定長メソッドで混在しているJavaのクラス(要するに↓のようなクラス)を
Scala/Kotlin/Javaから引数1個で呼び出した場合に、どちらが呼ばれるかを調査した。
public class CalledClazz {
public static void method(Object... args){
System.out.println("可変長引数のメソッドです" + Arrays.toString(args));
}
public static void method(Object args){
System.out.println("固定長引数のメソッドです" + args);
}
}
結論
引数1個渡した際の挙動
- Java => 基本は、
method(Object args)
が実行されるが、一部のケースで変な挙動になる - Kotlin =>
method(Object args)
が実行される - Scala => 可変長引数のほう
method(Object... args)
が呼ばれてしまう…ので、Scalaから固定長引数のJavaメソッドを呼ぶ方法も検討した。
調査方法(調査コード)
Javaから呼び出し
public class JavaClazz {
public static void main(String... args) throws IOException {
CalledClazz.method("Java");
CalledClazz.method(null);
Object s = null;
CalledClazz.method(s);
CalledClazz.method((Object)null);
}
}
実行結果
固定長引数のメソッドですJava
可変長引数のメソッドですnull
固定長引数のメソッドですnull
固定長引数のメソッドですnull
nullを渡すと、可変長引数のほうにくる?
しかも、要素数1のObject型の配列ではなく、nullが渡る…。
しかもしかも、nullでなんらかの型で変数宣言して渡すか、Objectへキャストすると固定長引数のメソッドにくる…。
Kotlinから呼び出し
fun main() {
CalledClazz.method("Kotlin")
CalledClazz.method(null)
}
実行結果
固定長引数のメソッドですKotlin
固定長引数のメソッドですnull
自然な挙動っぽく見える。
Scalaから呼び出し
object CallTestByScala extends App {
CalledClazz.method("Scala")
CalledClazz.method(null)
}
実行結果
可変長引数のメソッドです[Scala]
可変長引数のメソッドです[null]
なんで配列になって、可変長引数のメソッドが呼ばれるんだろう。
Scalaから固定長引数のJavaメソッドを呼ぶ方法
val f: Object => Unit = CalledClazz.method
f("Scala")
固定長引数のメソッドですScala
とでるようになりました。
こうやって、型推論に任せないで、関数の型を宣言すると意図通りに呼ばれるようだ。
他にも方法があるんだろうか?
ちなみに、
val f: Object => Unit = CalledClazz.method
f("Scala", "aaaaa")
とやると、固定長引数のほうのメソッドが呼ばれ、Tuple2型の1つの値が渡され、
固定長引数のメソッドです(Scala,aaaaa)
と出ます。
オマケ
method(Object... args)
はScalaから見ると、反復パラメータという扱いらしく、
scala.List
をJavaの可変長メソッドへ 意図したとおりに 渡したい場合は、
CalledClazz.method(List("a","b","b"): _*)
とする必要があります。
つけないと、List("a","b","b")
というオブジェクト1個を要素とした、要素数1の配列という扱いになるようで、
可変長引数のメソッドです[List(a, b, b)]
と出てしまう。