背景
先日とあるプロジェクトのヘルプに入りました。環境的にはjava8と聞いていたので、普通にラムダ式を使って実装、テストコードを書き、普通に「mvn test」を実行したのですが、なぜかエラーになりました。どうやらmavenのコンパイラの設定がjava7になっていたようです(最初はCheckstyleのエラーで引っかかっていたのでなんだこれ?と思っていました)。
それじゃあ仕方ないなとjava7の文法で実装することにしたのですが、java8のメソッドを使っている箇所があることに気づきました。つまりjava8の構文は使えないが、java8のメソッドは使えるということです。開発自体はjava7の文法で実装して終わらせたのですが、ふと、ラムダ式は使えなくてもjava8のメソッドが使えるのであればStream API自体は使えるのではないかと考えました。そこで今回ちょっと試してみたいと思います。
前置きが長くなりましたが要約すると以下となります。
- mavenのコンパイラの設定(pom.xml)的にはjava7
- 実行環境的にはjava8、コンパイルの際に使われるのもjava8
- であれば、ラムダ式は使えなくてもStream APIは使えるのでは?
- 試してみる
準備
mavanとjava8のインストール
現象を再現するために、まずはmavenとjava8のインストールをします。
この作業は別途以下に記載しています。
作成物
サンプルプログラム
現象再現のため以下のようなメソッドを用意しました。
public List<String> sample(List<String> list) {
return list.stream().map(s -> s + ":test").collect(Collectors.toList());
}
テストコード
確認用のテストコードも以下のように準備しました。
(ただこの内容自体はあまり重要ではないです)
@Test
void test() {
List<String> input = Arrays.asList("a", "b", "c");
List<String> expected = Arrays.asList("a:test", "b:test", "c:test");
assertThat(new Sample().sample(input), is(expected));
}
mavenの設定(pom.xml)
まずは用意したサンプルプログラムが動作することを確認するために、以下のような設定にしておきます。java8の設定にしているので、この状態であれば動くはずです。
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
実行
作成したものを使って、実際に動かしてみます。
$ mvn clean test
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------< masaki-blog:maven-java7-stream-api >-----------------
[INFO] Building maven-java7-stream-api 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ maven-java7-stream-api ---
[INFO] Deleting /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-java7-stream-api ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ maven-java7-stream-api ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven-java7-stream-api ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ maven-java7-stream-api ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ maven-java7-stream-api ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running net.masaki_blog.sample.SampleTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.012 s - in net.masaki_blog.sample.SampleTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.922 s
[INFO] Finished at: 2020-03-02T23:44:54+09:00
[INFO] ------------------------------------------------------------------------
$
成功しました。
現象の再現
次に現象の再現をします。
mavenの設定(pom.xml)
mavenの設定を以下のように変更します。コンパイラーの設定をjava7にしています。
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
実行
設定を変えた状態で動かしてみます。
$ mvn clean test
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------< masaki-blog:maven-java7-stream-api >-----------------
[INFO] Building maven-java7-stream-api 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ maven-java7-stream-api ---
[INFO] Deleting /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-java7-stream-api ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ maven-java7-stream-api ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/target/classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/src/main/java/net/masaki_blog/sample/Sample.java:[8,36] ラムダ式は-source 1.7でサポートされていません
(ラムダ式を使用可能にするには、-source 8以上を使用してください)
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.631 s
[INFO] Finished at: 2020-03-02T23:45:37+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile (default-compile) on project maven-java7-stream-api: Compilation failure
[ERROR] /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/src/main/java/net/masaki_blog/sample/Sample.java:[8,36] ラムダ式は-source 1.7でサポートされていません
[ERROR] (ラムダ式を使用可能にするには、-source 8以上を使用してください)
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
$
エラーになり現象が再現できました。ラムダ式が使えないと言うメッセージも出力されています。
修正プログラム
先ほどのmaven設定でも動くように修正したプログラムが以下です。
private static final Collector<String, ?, List<String>> TO_LIST = Collectors.toList();
public List<String> sample(List<String> list) {
return list.stream().map(this.convert()).collect(TO_LIST);
}
private Function<String, String> convert() {
return new Function<String, String>() {
@Override
public String apply(String s) {
return s + ":test";
}
};
}
解説
Function
Stream#mapの引数はFunctionなので、mapの引数にFunctionのインスタンスを渡すようにしています。Functionのapplyメソッドの実装で変換する内容を実装しています。
Collector
Stream#collectの引数に渡すCollectorですが、java8であればそのまま「Collectors.toList()」を渡せば良いですが、java7にしていると以下のようなエラーが発生します。
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ maven-java7-stream-api ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/target/classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/src/main/java/net/masaki_blog/sample/Sample.java:[12,57] 不適合な型: java.util.List<java.lang.Object>をjava.util.List<java.lang.String>に変換できません:
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.623 s
[INFO] Finished at: 2020-03-02T23:47:56+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile (default-compile) on project maven-java7-stream-api: Compilation failure
[ERROR] /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/src/main/java/net/masaki_blog/sample/Sample.java:[12,57] 不適合な型: java.util.List<java.lang.Object>をjava.util.List<java.lang.String>に変換できません:
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
上記を避けるために、一旦変数に置いています。
なお、これは以下のようにCollectorsの後(と言うよりはtoListの前)に型を指定することでも回避可能です。
public List<String> sample(List<String> list) {
return list.stream().map(this.convert()).collect(Collectors.<String>toList());
}
実行
修正後のプログラムで実行すると以下のようになります。
$ mvn clean test
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------< masaki-blog:maven-java7-stream-api >-----------------
[INFO] Building maven-java7-stream-api 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ maven-java7-stream-api ---
[INFO] Deleting /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven-java7-stream-api ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ maven-java7-stream-api ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven-java7-stream-api ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ maven-java7-stream-api ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/masaki/own/git/masaki-code/java/maven-java7-stream-api/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ maven-java7-stream-api ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running net.masaki_blog.sample.SampleTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.036 s - in net.masaki_blog.sample.SampleTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.478 s
[INFO] Finished at: 2020-03-02T23:51:06+09:00
[INFO] ------------------------------------------------------------------------
$
成功しました。
修正プログラム(改)
先ほどの修正プログラムを1メソッドの中に収めたものが以下です。
public List<String> sample(List<String> list) {
return list.stream().map(new Function<String, String>() {
@Override
public String apply(String s) {
return s + ":test";
}
}).collect(Collectors.<String>toList());
}
これも実行結果は先ほどと同様になります。
まとめ・感想
mavenのコンパイラの設定(pom.xml)がjava7になっていても、Stream APIは使えることが確認できました。
個人的には面白い結果が得られました。ただ、実用性で言うと微妙かもしれないです。
と言うのも、今回のプログラムだと、単純にjava7で書いたものとあまりスマートさが変わらないからです。
public List<String> sample(List<String> list) {
List<String> ret = new ArrayList<>();
for (String s : list) {
ret.add(s + ":test");
}
return ret;
}
Stream APIを使う明確なメリットが無ければ、素直にjava7での実装をした方が良いかもしれないです。
作成物
今回の作成物は以下に置いています。
https://github.com/masaki-code/java/tree/master/maven-java7-stream-api
コメント