Java 8コードをコンパイルしてJava 7 JVMで実行できますか?


163

Java 8では、ラムダ式などの重要な新しい言語機能が導入されています。

これらの言語の変更は、コンパイルされたバイトコードの大幅な変更を伴うため、レトロトランスレーターを使用せずにJava 7仮想マシンで実行できなくなりますか?


回答:


146

いいえ、ソースコードで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

4
いいえ、そうは思いません。Javaはデスクトップ市場でわずかなシェアを持っていますが、かなり小さなグリップでその小さなシェアを維持しています。しかし、それは新しいバージョンや機能の採用を妨げます。ローカルのJavaインストールをアップグレードしなくて済むようにしたいので、私が書いているコードではJava 8の機能をかなり長い間使用できません。
JesperE 2014

どうして?「はい」は、Java 8をコンパイルしてJava 7 VMで実行できることを意味します。これは、Java 8コンパイラーによれば正しくありません。
JesperE 2014年

5
今私は見る:あなたの「いいえ」は質問の本文ではなく、質問の見出しに答えます。
Abdull 2014年

58

デフォルトのメソッドでは、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-Backportstreamsupportなど)。

要約、デフォルトのメソッドには新しいJVM機能が必要ですが、他の言語機能には必要ありません。それらを使用する場合は、Java 8でコードをコンパイルし、Retrolambdaを使用してバイトコードをJava 5/6/7形式に変換する必要があります。少なくともバイトコードのバージョンを変更する必要があり、javacは許可しない-source 1.8 -target 1.7ため、レトロトランスレーターが必要です。


3
実際、型注釈は実行時に表示できます。stackoverflow.com/questions/22374612/…–
アンチモン

33

私の知る限り、JDK 8のこれらの変更では、新しいバイトコードを追加する必要はありませんでした。ラムダインストルメンテーションの一部は、invokeDynamic(JDK 7にすでに存在する)を使用して行われています。したがって、JVM命令セットの観点から見ると、コードベースに互換性がなくなることはありません。ただし、以前のJDKでJDK 8のコードをコンパイル/実行するのを困難にする可能性のあるAPI関連およびコンパイラーの改善点はたくさんあります(ただし、私はこれを試していません)。

たぶん、次の参考資料は、ラムダに関連する変更がどのように計測されているかについての理解を深めるのに何らかの形で役立つでしょう。

これらは、物がフードの下でどのように計装されるかを詳細に説明しています。おそらくそこにあなたの質問への答えを見つけることができます。


7
新しいバイトコードはありませんが、新しい構造です。検証者は吐き出します。
ジョナサンS.フィッシャー

12
良い例がインターフェースです。メソッドを含めることができるようになりました。Java7ベリファイアはこれを処理するために装備されていません。古いバイトコードはすべて使用されていますが、新しい方法で使用されています。
ジョナサンS.フィッシャー

1
非常に多くの言語機能を備えたscalaコンパイラーが、jdk5のターゲットjvmリリースをどのように実現できるのでしょうか。
マリノス

1
@MarinosAn正確にはどういう意味ですか?具体的なメソッドを含む特性を持つMIは、たとえばclass C extends A with B、通常のインターフェースAB、それに付随するクラスA$classとで実装されますB$class。クラスCは、メソッドを静的コンパニオンクラスに転送するだけです。自己型はまったく強制されず、ラムダはコンパイル時に内部クラスを抽象化するために変換されるため、new D with A with B式もそうです。パターンマッチングは、if-else構造の集まりです。非ローカルリターン?ラムダからのtry-catchメカニズム。何か残ってる?(興味深いことに、私のscalacは1.6がデフォルトであると言っています)
Adowrath 2017

1
もちろん、自己型などは特別なクラス属性と注釈にエンコードされているので、既にコンパイルされているクラスを使用するときにscalacはルールを使用および適用できます。
Adowrath 2017


-5

-source 1.7 -target 1.7コンパイルできます。しかし、ラムダなどのJava 8固有の機能がある場合はコンパイルされません


この質問は、新しい言語機能の使用を明確に示しているので、-source 1.7飛ばないでください。
toolforger
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.