Deep understanding of Lambda expressions
1、背景
Java 8 のラムダ式は、もはや「新機能」ではありません。
今では多くの開発者が業務でラムダ式を使用しています。
しかし、ラムダ式の内部原理を本当に理解しているでしょうか?
2、分析
以下は Stream を使った非常にシンプルなコードです。
Jclasslib プラグイン (「IDEA Plug-ins Who Meet Later」で紹介) を使用して、バイトコードを確認します。
javac と javap コマンドを使用して、コマンドラインで実行することもできます。
例:
javac ListDemo.java
javap -p -s -c -v -l ListDemo
内部クラスと BootstrapMethods が追加されていることがわかります。
lambda$main$0
0 aload_ 0
1 ldc #20
3 invokevirtual #21
6 ireturn
以下のコードと等価です:
private static boolean lambda$main$0(String name){
return name.startsWith("tom");
}
lambda$main$1
0 aload_ 0
1 invokevirtual #19
4 areturn
以下のコードと等価です:
private static String lambda$main$1(String name){
return name.toLowerCase();
}
以上の簡単な分析から、ラムダ式は最終的にプライベートな静的メソッドとしてコンパイルされることがわかります。
invokedynamic 命令を通じて動的メソッド呼び出しを実行します。
以下の通りです。
50 invokedynamic #10
JVM ドキュメントから、この命令が動的呼び出し計算を実現するために使用されることがわかります。定数プールのキーに対応するシンボル参照を通じて動的計算呼び出しに変換され、CallSite インスタンスがターゲットメソッド呼び出しの実際のインスタンスとなります。
プラグインを使用すると、この呼び出しチェーンをいつでも確認できます。
invokedynamic #10 の定数プール内の #10 は以下の通りです:
BootstrapMethods #0 に対応するのは以下の通りです:
これは java.lang.invoke.LambdaMetafactory#metafactory の呼び出しであることがわかります。戻り値は java.lang.invoke.CallSite オブジェクトであり、実際のターゲットメソッド呼び出しを表します。
3、発展
以上の解説で、ラムダ式についてより深く理解していただけたと思います。
Map.forEach の内部実装を推測して、検証してみてください。
自分で論理的に等価なコードを書いてみてください。
ラムダ式を見たときに、その内部実装の仕組みを補完できることを期待します。
4、まとめ
多くの知識は当然のことと思われがちですが、深く掘り下げることで新たな知見を得ることができます。
知識を本当に理解して初めて、面接で十分な自信を持つことができます。
また、ラムダ式は便利ですが、貪欲になりすぎてはいけません。ラムダ式の乱用は、コードの可読性と保守性を損ないます。
Java 8 のラムダ式は、もはや「新機能」ではありません。
今では多くの開発者が業務でラムダ式を使用しています。
しかし、ラムダ式の内部原理を本当に理解しているでしょうか?
2、分析
以下は Stream を使った非常にシンプルなコードです。
Jclasslib プラグイン (「IDEA Plug-ins Who Meet Later」で紹介) を使用して、バイトコードを確認します。
javac と javap コマンドを使用して、コマンドラインで実行することもできます。
例:
javac ListDemo.java
javap -p -s -c -v -l ListDemo
内部クラスと BootstrapMethods が追加されていることがわかります。
lambda$main$0
0 aload_ 0
1 ldc #20
3 invokevirtual #21
6 ireturn
以下のコードと等価です:
private static boolean lambda$main$0(String name){
return name.startsWith("tom");
}
lambda$main$1
0 aload_ 0
1 invokevirtual #19
4 areturn
以下のコードと等価です:
private static String lambda$main$1(String name){
return name.toLowerCase();
}
以上の簡単な分析から、ラムダ式は最終的にプライベートな静的メソッドとしてコンパイルされることがわかります。
invokedynamic 命令を通じて動的メソッド呼び出しを実行します。
以下の通りです。
50 invokedynamic #10
JVM ドキュメントから、この命令が動的呼び出し計算を実現するために使用されることがわかります。定数プールのキーに対応するシンボル参照を通じて動的計算呼び出しに変換され、CallSite インスタンスがターゲットメソッド呼び出しの実際のインスタンスとなります。
プラグインを使用すると、この呼び出しチェーンをいつでも確認できます。
invokedynamic #10 の定数プール内の #10 は以下の通りです:
BootstrapMethods #0 に対応するのは以下の通りです:
これは java.lang.invoke.LambdaMetafactory#metafactory の呼び出しであることがわかります。戻り値は java.lang.invoke.CallSite オブジェクトであり、実際のターゲットメソッド呼び出しを表します。
3、発展
以上の解説で、ラムダ式についてより深く理解していただけたと思います。
Map.forEach の内部実装を推測して、検証してみてください。
自分で論理的に等価なコードを書いてみてください。
ラムダ式を見たときに、その内部実装の仕組みを補完できることを期待します。
4、まとめ
多くの知識は当然のことと思われがちですが、深く掘り下げることで新たな知見を得ることができます。
知識を本当に理解して初めて、面接で十分な自信を持つことができます。
また、ラムダ式は便利ですが、貪欲になりすぎてはいけません。ラムダ式の乱用は、コードの可読性と保守性を損ないます。
Related Articles
-
A detailed explanation of Hadoop core architecture HDFS
Knowledge Base Team
-
What Does IOT Mean
Knowledge Base Team
-
6 Optional Technologies for Data Storage
Knowledge Base Team
-
What Is Blockchain Technology
Knowledge Base Team
Explore More Special Offers
-
Short Message Service(SMS) & Mail Service
50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00
