匿名クラスとラムダ式で作られるクラスファイルの違い
短いエントリーです。
ご存知の方も多いと思いますが、従来Runnable
やComparator
のような関数様パラメーターのメソッドローカル実装は匿名クラスで賄っていましたが、Java8からはラムダ式で書けるようになりました。
匿名クラスは、エンクロージングクラス名+$1.class
(例:App$1.class
)のような名前でクラスファイルが作られます。数字は連番で、2つ目は$2.class
となります。
ラムダ式は、エンクロージングクラスのクラスファイル内部に合成メソッドとして作成されます。メソッド名は、lambda$
+エンクロージングメソッド名+$1
(例:lambda$main$1
)のような名前になります。数字が連番なのは同じです。
それでは、実際に作成されるクラスファイルはどのような違いがあるのでしょうか。
簡単なサンプルコードで試してみました。
- 匿名クラスバージョン(
App1.java
)
import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.ForkJoinPool; public final class App1 { public static void main(String[] args) { Callable<String> task1 = new Callable<String>() { @Override public String call() throws Exception { return "task1"; } }; Callable<String> task2 = new Callable<String>() { @Override public String call() throws Exception { return "task2"; } }; Callable<String> task3 = new Callable<String>() { @Override public String call() throws Exception { return "task3"; } }; ForkJoinPool.commonPool().invokeAll(Arrays.asList(task1, task2, task3)); } }
- ラムダ式バージョン(
App2.java
)
import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.ForkJoinPool; public final class App2 { public static void main(String[] args) { Callable<String> task1 = () -> { return "task1"; }; Callable<String> task2 = () -> { return "task2"; }; Callable<String> task3 = () -> { return "task3"; }; ForkJoinPool.commonPool().invokeAll(Arrays.asList(task1, task2, task3)); } }
- それぞれのファイルサイズ
App1.java bytes file name 647 App1$1.class 647 App1$2.class 647 App1$3.class 724 App1.class 2,451 app.jar App2.java bytes file name 1,482 App2.class 1,229 app.jar
単純計算すると、1つの匿名クラス当たり400バイト減っています。これは、100の匿名クラスをラムダ式に置き換えたとしたら、39KB程度減る計算になります。
サイズを減らすためにラムダ式を使うなんてことはまず無いと思いますが、なんとなく得した気分になるというお話でした。
(おわり)