Java 8 では、ラムダ式などの重要な新しい言語機能が導入されています。
言語のこれらの変更は、コンパイルされたバイトコードの大幅な変更を伴うため、レトロトランスレータを使用しないと Java 7 仮想マシンで実行できなくなるのでしょうか?
Java 8 では、ラムダ式などの重要な新しい言語機能が導入されています。
言語のこれらの変更は、コンパイルされたバイトコードの大幅な変更を伴うため、レトロトランスレータを使用しないと Java 7 仮想マシンで実行できなくなるのでしょうか?
いいえ、ソース コードで 1.8 機能を使用するには、1.8 VM をターゲットにする必要があります。新しい Java 8 リリースを試し、 でコンパイルしようとしましたが-target 1.7 -source 1.8
、コンパイラは拒否しました:
$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8
デフォルトのメソッドでは、バイトコードと JVM に、Java 7 では不可能だったような変更が必要です。Java 7 以下のバイトコード ベリファイアは、メソッド本体を持つインターフェイスを拒否します (静的初期化メソッドを除く)。デフォルト メソッドはサブクラスでオーバーライドできるため、呼び出し側で静的メソッドを使用してデフォルト メソッドをエミュレートしようとしても、同じ結果にはなりません。Retrolambdaは、デフォルト メソッドのバックポートのサポートを制限していますが、新しい JVM 機能が本当に必要なため、完全にバックポートすることはできません。
ラムダは、必要な API クラスがそこに存在する場合、Java 7 でそのまま実行できます。invokedynamic 命令は Java 7 に存在しますが、コンパイル時にラムダ クラスを生成するようにラムダを実装することは可能でした (初期の JDK 8 ビルドではそのように行われました)。その場合、どの Java バージョンでも機能します。(オラクルは、将来の証明のためにラムダにinvokedynamicを使用することを決定しました。おそらくいつかJVMにファーストクラスの関数が含まれるので、すべてのラムダのクラスを生成する代わりに、それらを使用するようにinvokedynamicを変更して、パフォーマンスを向上させることができます。)Retrolambdaが行うことはこれらすべての invokedynamic 命令を処理し、それらを匿名クラスに置き換えること。lamdba invokedynamic が初めて呼び出されたときに Java 8 が実行時に行うことと同じです。
注釈の繰り返しは単なる構文糖衣です。以前のバージョンと互換性のあるバイトコードです。Java 7 では、ヘルパー メソッド ( getAnnotationsByType など) を自分で実装するだけで済みます。これは、繰り返されるアノテーションを含むコンテナー アノテーションの実装の詳細を非表示にします。
私の知る限り、型注釈はコンパイル時にのみ存在するため、バイトコードの変更は必要ないため、Java 8 でコンパイルされたクラスのバイトコード バージョン番号を変更するだけで、Java 7 で動作させることができます。
メソッドのパラメータ名は Java 7 のバイトコードに存在するため、これにも互換性があります。メソッドのバイトコードを読み取り、メソッドのデバッグ情報でローカル変数名を調べることで、それらにアクセスできます。たとえば、Spring Framework は@PathVariableを実装するために正確にそれを行うため、おそらく呼び出すことができるライブラリ メソッドがあります。抽象インターフェイス メソッドにはメソッド本体がないため、そのデバッグ情報は Java 7 のインターフェイス メソッドには存在せず、AFAIK も Java 8 には存在しません。
その他の新機能は、主に新しい API、HotSpot およびツールの改善です。新しい API の一部は、サード パーティのライブラリとして利用できます ( ThreeTen-Backportやstreamsupport など)。
要約すると、デフォルトのメソッドには新しい JVM 機能が必要ですが、他の言語機能には必要ありません。それらを使用する場合は、Java 8 でコードをコンパイルしてから、Retrolambda を使用してバイトコードを Java 5/6/7 形式に変換する必要があります。少なくともバイトコードのバージョンを変更する必要があり、javac は許可しない-source 1.8 -target 1.7
ため、レトロトランスレータが必要です。
私の知る限り、JDK 8 でのこれらの変更のいずれも、新しいバイトコードの追加を必要としませんでした。invokeDynamic
ラムダ インストルメンテーションの一部は、 (JDK 7 に既に存在する) を使用して行われています。したがって、JVM 命令セットの観点からは、コードベースの互換性を損なうものは何もありません。ただし、JDK 8 のコードを以前の JDK でコンパイル/実行するのが困難になる可能性のある API 関連およびコンパイラの改善が多数あります (ただし、私はこれを試していません)。
おそらく、次の参考資料は、ラムダに関連する変更がどのように計測されているかについての理解を深めるのに役立つでしょう。
これらは、物事が内部でどのように計測されているかを詳細に説明しています。あなたの疑問の答えがそこにあるかもしれません。
「retrotranslator」を使用する場合は、Esko Luontola の優れた Retrolambda を試してください: https://github.com/orfjackal/retrolambda
そう-source 1.7 -target 1.7
すればコンパイルされます。ただし、ラムダなどのJava 8固有の機能がある場合はコンパイルされません