System.out.println("lambdaisrun") という形式のコードということですか?実際には、内部クラスはどうですか? の確立です。実際、これは最も単純なラムダ式です。"/> System.out.println("lambdaisrun") という形式のコードということですか?実際には、内部クラスはどうですか? の確立です。実際、これは最も単純なラムダ式です。">

ホームページ >Java >&#&チュートリアル >JavaでLambdaソースコードを読む方法

JavaでLambdaソースコードを読む方法

王林
王林転載
2023-05-13 11:10:061454ブラウズ

JavaでLambdaソースコードを読む方法


1. デモ

まず、以下に示すラムダ式のデモを見てみましょう。

JavaでLambdaソースコードを読む方法

コードは比較的単純で、新しいスレッドを開始して文を出力するだけですが、画像については () -> System.out.println (" lambda が実行されます) 「) これ 多くの学生がこの種のコードについて混乱していると推定されます。Java はこの種のコードをどのように識別するのでしょうか?

記述方法を匿名内部クラスに変更すると、以下のように非常に明確になり、誰でも理解できるようになります。 () -> System.out.println (“ lambda が実行される”) この形式のコードは実際に内部クラスを作成しますか?実際、これは最も単純な Lambda 式です。IDEA ではソース コードとその基礎となる構造を確認できません。ここでは、その基礎となる実装を確認するいくつかの方法を紹介します。

2. 例外判定方法JavaでLambdaソースコードを読む方法

コード実行中に能動的に例外をスローし、スタックを出力することができます。スタックはその実行軌跡を説明します。一般に、この方法はシンプルで効率的であり、基本的には、以下に示すように、多くの場合に隠しコードを試してみましょう:

例外スタックから、JVM が現在のクラスの内部クラスを自動的に作成することがわかります。 class (エラースタックに複数回出現する $ は内部クラスを示します) 内部クラスのコードの実行中に例外がスローされますが、ここに示されているコードは不明なソースであるため、デバッグできません。 , 例外 どちらもコードの実行パスを公開でき、ブレークポイントを設定して再度実行することができますが、ラムダ式の場合は例外判定メソッドを通じて、内部クラスの存在が分かるだけで、内部クラスのソースコードを見ることはできません。クラス。

3. javap コマンドの方法JavaでLambdaソースコードを読む方法

javap は、クラスのバイトコード ファイルを表示するために Java に付属するツールで、Java 基本環境がインストールされているコンピュータでは、以下に示すように javap コマンドを直接実行できます。

コマンド オプションのうち、バイトコード ファイルの内容を完全に出力するには、主に -v -verbose コマンドを使用します。

次に、javap コマンドを使用して Lambda.class ファイルを表示します。説明の中で、クラス ファイルに関する知識も取り入れます。 JavaでLambdaソースコードを読む方法

コマンド ウィンドウで Lambda.class の場所を見つけ、コマンド javap -verbose Lambda.class を実行すると、長いリストが表示されます。これらはアセンブリ命令と呼ばれます。実行してみましょう。次に説明しましょう (すべての参考資料は Java 仮想マシン仕様から引用されており、1 つずつ引用する必要はありません):

アセンブリ命令では、Constant で始まる型の長いリストを簡単に見つけることができます。正式には英語では Run-Time Constant Pool と呼ばれますが、単純に定数が詰め込まれたテーブルと理解しています。テーブルにはコンパイル時の明確な数値とテキスト、クラス、メソッド、およびクラスの型情報が含まれています。フィールドなどテーブル内の各要素は cp

info と呼ばれます。cp

info は一意の識別 (タグ) 名で構成されます。現在、合計のタグ タイプは次のとおりです:

投稿 これは私たちが解析した画像の一部です:

JavaでLambdaソースコードを読む方法

画像内の「定数プール」という言葉は、現在の情報が定数プール; JavaでLambdaソースコードを読む方法

  1. 各行は

    cp_info

    で、最初の列の #1 は定数プール内で 1 としてマークされた位置を表します;
  2. 各行の列の 2 番目は、cp_info

    の一意の識別子 (タグ) です。たとえば、Methodref は、上の表の CONSTANT_Methodref に対応します (上の表の値はタグに対応します)これは、現在の行がメソッドの名前、入力パラメータのタイプ、出力パラメータのタイプなどのメソッドの説明情報を表すことを意味します。具体的な意味は、Java 仮想マシンの仕様で見つけることができます。 Methodref のスクリーンショットは次のとおりです:

  3. 各行の 3 列目に、特定の値の場合、その特定の値が直接表示されます。これは複素数値であり、JavaでLambdaソースコードを読む方法cp_info

    への参照が表示されます。たとえば、図では赤で 2 とマークされています。13 と 14 の位置にある 2 つの
  4. cp_info
  5. を参照します。13 は、メソッドが名前は init、14 はメソッドに戻り値がないことを示します。メソッドの名前と戻り値の型の組み合わせは、パラメーターなしのコンストラクターを示します。

    各行の 4 番目の列は次のとおりです。特定の値。

  6. より重要な cp_info タイプについては、その意味を説明します:

    1. InvokeDynamic は後で詳しく説明する動的呼び出しメソッドを表し、

    2. Fieldref はフィールドの名前や名前などの説明情報を表します。フィールドのタイプ;

    3. NameAndType はフィールドとメソッドのタイプの説明;

    4. MethodHandle メソッド ハンドル、動的に呼び出すための一般名メソッド、コンパイル時には特定のメソッドはわかりません そのメソッドですが、実行時にどのメソッドが呼び出されるのかは確実にわかります;

    5. MethodType 動的メソッドのタイプ、わかることだけですそのメソッドのタイプは動的に実行するときです。

    上の図の赤でマークされた 3 つの場所から、このコードに類似した Ljava/lang/invoke/MethodHandles$Lookup、java/lang/invoke/LambdaMetafactory.metafactory が見つかりました。 MethodHandles と LambdaMetafactory は java.lang.invoke パッケージの重要なメソッドです。invoke パッケージは主に動的言語の機能を実装します。Java 言語が静的コンパイル言語であることはわかっています。コンパイル中に、クラス、メソッド、フィールドのタイプが決まります。 、などが決定されており、 invoke は動的言語を実装します。つまり、クラス、メソッド、およびフィールドの型はコンパイル時には不明で、実行時にのみわかります。

    たとえば、次のコード行: Runnable runnable = () -> System.out.println(“lambda is run”); コンパイラが () をコンパイルするとき、コンパイラはこの括弧が何であるかを認識しません。を実行するときにのみ、これが Runnable.run() メソッドを表していることがわかります。 invoke パッケージ内の多くのクラスは、これらの () を表すように設計されており、これをメソッド ハンドル (MethodHandler) と呼びます。コンパイル時、コンパイラはこれがメソッド ハンドルであることのみを認識し、どのメソッドが実際に実行されるかは知りません。それはそのときになってみないと分からないので、問題は、JVM が実行するときに、() メソッド ハンドルが実際に Runnable.run() メソッドを実行していることをどのようにして知ることができるのかということです。

    まず、単純なメソッドのアセンブリ命令を見てみましょう。

    JavaでLambdaソースコードを読む方法

    上の図から、() -> がわかります。シンプルなメソッドのシステム out.println(“lambda is run”) コード内の () は、実際には Runnable.run メソッドです。

    上の図で赤 1 とマークされている #2 定数プールまで遡ります。InvokeDynamic は、これが動的呼び出しであることを示します。呼び出しは 2 つの定数プールの cp_info です。位置は # です。 0:#37。#37 は、// run:()Ljava/lang/Runnable を意味します。これは、JVM が実際に実行されるときに、Runnable.run() メソッドを動的に呼び出す必要があることを示します。アセンブリ命令を見ると () 実際には Runnable.run() であることがわかります。それを証明するために以下をデバッグしてみましょう。

    上の図の 3 か所で LambdaMetafactory.metafactory という単語が見つかりました。公式ドキュメントを参照すると、このメソッドが実行中に実際のコードにリンクするための鍵であることがわかりました。以下に示すように、ブレークポイントを使用してデバッグします:

    JavaでLambdaソースコードを読む方法

    metafactory メソッドの入力パラメータ caller は動的呼び出しが実際に発生する場所を表し、invokedName は呼び出しメソッドの名前を表します。 、invokedType は呼び出しの複数の入力を表します。パラメータと出力パラメータ、samMethodType は特定の実装者のパラメータを表し、implMethod は実際の実装者を表し、instantiatedMethodType は implMethod と同等です。

    上記を要約すると:

    1: アセンブリ命令の単純なメソッドから、Runnable.run メソッドが実行されることがわかります;

    2:実際の操作 JVM が単純なメソッドの invokedynamic 命令に遭遇すると、動的に LambdaMetafactory.metafactory メソッドを呼び出し、特定の Runnable.run メソッドを実行します。

    したがって、ラムダ式値の特定の実行は、invokedynamic JVM 命令に起因すると考えられます。コンパイル時に何をすべきかはわかりませんが、まさにこの命令のおかげで、特定の実行を見つけることができます。コードを動的に実行する場合。

    それでは、アセンブリ命令の出力の最後を見てみますと、以下のような例外判定メソッドに内部クラスが存在します。 #上の図では矢印が多く、現在の内部クラスの情報がすべてレイヤーごとにわかりやすく表現されています。

以上がJavaでLambdaソースコードを読む方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。