今更ですが、Groovyでコンパイル時に型をチェックするする方法をまとめました。
import groovy.transform.TypeChecked
import groovy.transform.CompileStatic
/**
* コンパイル時点でエラーが出れば、態々実行してテストする必要がなくなるのは自明。
* 例えば下記のreturnCheck()メソッドの場合、ifの両方の条件を通るテストをしないと、
* 意図していない型の値が返されるというバグが分からない。
* (returnCheck()場合はIntegerを返す筈なのにStringが帰えされるバグがある)
*
* @TypeCheckedはコンパイル時に型をチェックするので、それが出来ない動的な機能は基本的に使えない。
* なお、コンパイル時点でチェックするのみで、生成されるバイトコードは@TypeCheckedが無いものと同じ。
* @CompileStaticを使うと、動的な機能が含まれないバイトコードになるため、基本的に実行の高速化・ファイル容量の減少が期待できる(はず)
*/
//@TypeChecked // コンパイル時にチェックするだけ。
@CompileStatic // コンパイル時にチェックし、生成するバイトコードも変化する(実行速度の向上)。
class Hoge {
def static main(args) {
// 存在しないメソッド・変数を実行する。
// これがコンパイルエラーに出来る。
unknownMethod()
}
// Integer型の戻り値のはずなのに、Stringが帰る可能性がある。
// これがコンパイルエラーにできる。
Integer returnCheck(b) {
if(b) {
1
} else {
"string"
}
}
// 引数aの型はStringなのに、Integer型のbに変数を代入している。
// メソッド引数でなくとも、普通の代入でも同様。
// これがコンパイルエラーに出来る
// なお、実行時にDate以外の値を渡すと、実行時例外となる。
def assignmentCheck(String a){
Integer b = a
Integer c = new String()
}
// 別のクラスのメソッドを呼ぶ際に、誤った型を渡す。
// これがコンパイルエラーにできる。
// "呼び出す側"に@TypeCheckedが宣言されている必要がある。
// 当然Mock#add()の引数が型付で宣言されている必要がある。
def callOther(){
def mock = new Mock()
mock.add(1,"2")
}
}
class Mock{
def add(Integer a, Integer b){
a + b
}
}