Apache Groovyのオーバロードと引数のデフォルト値の罠?
もう7年ほど仕事でApache Groovyを利用しているにもかかわらず、全く知らなかった動作を発見しました。 もしかしたらバグなのかも? 最新バージョンだとまた違う挙動になるかもしれません。(未確認)
具体的には、メソッドが一つしか無い場合、つまりオーバロードされていない場合、呼び出す際に引数を渡さないとその引数がデフォルト値(null
)で初期化されるということです。
起きたこと
実際にソースを見てみると一目瞭然です。
class Hoge {
String m1(String a) {
"A"
}
}
def a = new Hoge()
assert "A" == a.m1()
このコード、実際に呼び出しているメソッドは引数なしのm1()
メソッドですが、そもそもHogeクラスには引数を取らないm1()
メソッドは存在しません。
なので、このようなコードを実行すると
groovy.lang.MissingMethodException: No signature of method: Hoge.m1() is applicable for argument types: () values: []
と言うような例外が発生するものだと思いこんでいました。
しかし、上記のコードを実行しても問題なく処理が終わります。
コレはつまり、値を渡されなかった引数がデフォルト値のnull
で初期化されたためです。
例外になる場合
実は、似たようなケースでもちゃんと例外になってくれるパターンもあります。
オーバロードしていて該当するシグネチャが無い場合
class Hoge {
String m1(String a) {
"A"
}
String m1(Integer a) {
"B"
}
}
def a = new Hoge()
assert "A" == a.m1()
これは、以下のような例外になります。
Exception thrown
org.codehaus.groovy.runtime.metaclass.MethodSelectionException: Could not find which method m1() to invoke from this list:
public java.lang.String Hoge#m1(java.lang.Integer)
public java.lang.String Hoge#m1(java.lang.String)
at ConsoleScript19.run(ConsoleScript19:12)
nullが入らない引数の型の場合
コレは、オーバロードしていなくても、引数がプリミティブ型を取る場合プリミティブ型にはnullが格納できないため、例外が発生します。
class Hoge {
String m1(int a) {
"A"
}
}
def a = new Hoge()
assert "A" == a.m1()
以下のような例外が投げられます。
Exception thrown
groovy.lang.MissingMethodException: No signature of method: Hoge.m1() is applicable for argument types: () values: []
Possible solutions: m1(int), is(java.lang.Object), any(), dump(), use([Ljava.lang.Object;), tap(groovy.lang.Closure)
at ConsoleScript22.run(ConsoleScript22:8)
まとめ
今日までまったくこのような挙動に気付きませんでした。。。 Java等の静的型付け言語であればコンパイル時に気付けるはずですが、動的型付け言語であるApache Groovyは実行時まで気付けません。
原因としては単純に引数を渡すことを忘れていただけだったのですが、かなり痛い思いをする結果となりました。
(想定していないデータ(null
)で処理が普通に進んでしまっていて原因の究明に時間がかかった)
公開日:2021/01/22