現象
Android でも λ を使える gradle-retrolambda をいれ、順調に無名クラスの冗長な構文を駆逐していきました。
現在ユニットテストは Android Unit Test を利用しているのですが、テストコードの一部を λ に変更して実行してみると以下の様なエラーが出てしまいました()
JAVA_HOME
に JDK 1.7 を設定している場合
Test.java:61: error: illegal start of expression
.filter(state -> state != State.UNINITIALIZED)
JAVA_HOME
に JDK 1.8 を設定している場合
An exception has occurred in the compiler (1.8.0_25). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport) after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report. Thank you.
com.sun.tools.javac.code.Symbol$CompletionFailure: class file for java.lang.invoke.MethodType not found
原因
./gradlew test --debug
で原因を探ってみるとコンパイル時に-source
-traget
オプションが指定されていないことがわかりました。
23:14:30.815 [DEBUG] [org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler] Compiler arguments: -d /app/build/test-classes/debug -g -encoding UTF-8 -bootclasspath /Applications/android-sdk/platforms/android-21/android.jar -classpath
...
どうやら、Retrolambda はcompileTestDebugJava
タスク時には JDK を 1.8 に差し替えてくれないようです。
解決方法
そこで、自ら Gradle 上で差し替える必要があります。
gradle-retrolambda の ソースコード を参考に doFirst
で JDK やパスを差し替えます。
tasks.withType(Test) {
project.tasks
.findAll { task -> (task.name ==~ /compileTest.*Java/) }
.each { task ->
task.doFirst {
def buildPath = "$project.buildDir/retrolambda"
def jarPath = "$buildPath/$project.android.compileSdkVersion"
def javac = "${project.retrolambda.tryGetJdk()}/bin/javac"
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
options.fork = true
options.compilerArgs += ["-bootclasspath", "$jarPath/android.jar"]
options.forkOptions.executable = javac
}
}
}
JAVA_HOME
に JDK 1.7 を設定している場合、テスト実行は行えるようになるのですが、以下の様なテスト結果が表示されてしまいます。
Test > initializationError FAILED
java.lang.UnsupportedClassVersionError
この場合、Gradle とクラスで Java のバージョンが違うためJAVA_HOME
に JDK 1.8 を設定してください。
以上でユニットテスト上でも λ が使えるようになります。
やったね!