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
デフォルトのメソッドでは、Java 7では不可能であったバイトコードとJVMへの変更が必要です。Java7以下のバイトコードベリファイアは、メソッド本体(静的初期化メソッドを除く)を持つインターフェースを拒否します。デフォルトメソッドはサブクラスでオーバーライドできるため、呼び出し側で静的メソッドを使用してデフォルトメソッドをエミュレートしようとしても、同じ結果は得られません。Retrolambdaはデフォルトメソッドのバックポートのサポートに制限がありますが、新しいJVM機能が本当に必要なため、完全にバックポートすることはできません。
必要なAPIクラスが存在する場合、ラムダはそのままJava 7で実行できます。invokedynamic命令はJava 7に存在しますが、コンパイル時にラムダクラスを生成するようにラムダを実装することが可能でした(初期のJDK 8ビルドはそのように行いました)。この場合、どのJavaバージョンでも動作します。(Oracleは将来の検証のためにラムダにinvokedynamicを使用することを決定しました。おそらくいつの日かJVMがファーストクラスの機能を持つようになるので、invokedynamicを変更して、すべてのラムダのクラスを生成する代わりにそれらを使用して、パフォーマンスを向上させることができます。それらのすべてのinvokedynamic命令を処理し、それらを匿名クラスに置き換えます。lambba invokedynamicが最初に呼び出されたときに、Java 8が実行時に行うことと同じです。
注釈の繰り返しは、単なる構文上の砂糖です。以前のバージョンとバイトコード互換です。Java 7 では、繰り返しのアノテーションを含むコンテナアノテーションの実装の詳細を非表示にするヘルパーメソッド(例:getAnnotationsByType)を自分で実装する必要があるだけです。
AFAIK、タイプアノテーションはコンパイル時にのみ存在するため、バイトコードを変更する必要はないため、Java 8でコンパイルされたクラスのバイトコードバージョン番号を変更するだけで、Java 7で機能するようになります。
メソッドパラメータ名はJava 7のバイトコードに存在するため、互換性もあります。メソッドのバイトコードを読み取り、メソッドのデバッグ情報でローカル変数名を確認することで、それらにアクセスできます。たとえば、Spring Frameworkは@PathVariableを実装するためにまさにそれを行うので、おそらく呼び出すことができるライブラリメソッドがあります。抽象インターフェースメソッドにはメソッド本体がないため、そのデバッグ情報はJava 7のインターフェースメソッドには存在せず、Java 8にもAFAIKは存在しません。
その他の新機能は、主に新しい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でJDK 8のコードをコンパイル/実行するのを困難にする可能性のあるAPI関連およびコンパイラーの改善点はたくさんあります(ただし、私はこれを試していません)。
たぶん、次の参考資料は、ラムダに関連する変更がどのように計測されているかについての理解を深めるのに何らかの形で役立つでしょう。
これらは、物がフードの下でどのように計装されるかを詳細に説明しています。おそらくそこにあなたの質問への答えを見つけることができます。
class C extends A with B
、通常のインターフェースA
とB
、それに付随するクラスA$class
とで実装されますB$class
。クラスC
は、メソッドを静的コンパニオンクラスに転送するだけです。自己型はまったく強制されず、ラムダはコンパイル時に内部クラスを抽象化するために変換されるため、new D with A with B
式もそうです。パターンマッチングは、if-else構造の集まりです。非ローカルリターン?ラムダからのtry-catchメカニズム。何か残ってる?(興味深いことに、私のscalacは1.6がデフォルトであると言っています)
「レトロトランスレーター」を使用する場合は、Esko Luontolaの優れたRetrolambdaを試してください。https://github.com/orfjackal/retrolambda
-source 1.7 -target 1.7
コンパイルできます。しかし、ラムダなどのJava 8固有の機能がある場合はコンパイルされません
-source 1.7
飛ばないでください。