web-dev-qa-db-ja.com

Java 11でコードを生成する方法ですが、ターゲットJava 8以上ですか?

私は小さなライブラリに取り組んでいます。明らかな理由で、[今すぐに推測するモジュールを除いて] Java 11のすべての機能を使用してコードを生成したいのですが、Java 8以上。

私がこれを試すとき:

javac -source 11 -target 1.8 App.Java

次のメッセージが表示されます。

warning: source release 11 requires target release 11

...バイトコードを見ると、クラスのバージョンが0x37(Java 11)であることがわかります。

$ xxd App.class
00000000: cafe babe 0000 0037 ...

そして、Java 8はそれをロードできません:

Exception in thread "main" Java.lang.UnsupportedClassVersionError: App has been
    compiled by a more recent version of the Java Runtime (class file version 55.0),
    this version of the Java Runtime only recognizes class file versions up to 52.0
    at Java.lang.ClassLoader.defineClass1(Native Method)
    at Java.lang.ClassLoader.defineClass(ClassLoader.Java:763)
    at Java.security.SecureClassLoader.defineClass(SecureClassLoader.Java:142)
    at Java.net.URLClassLoader.defineClass(URLClassLoader.Java:468)
    at Java.net.URLClassLoader.access$100(URLClassLoader.Java:74)
    at Java.net.URLClassLoader$1.run(URLClassLoader.Java:369)
    at Java.net.URLClassLoader$1.run(URLClassLoader.Java:363)
    at Java.security.AccessController.doPrivileged(Native Method)
    at Java.net.URLClassLoader.findClass(URLClassLoader.Java:362)
    at Java.lang.ClassLoader.loadClass(ClassLoader.Java:424)
    at Sun.misc.Launcher$AppClassLoader.loadClass(Launcher.Java:349)
    at Java.lang.ClassLoader.loadClass(ClassLoader.Java:357)
    at Sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.Java:495)

どのようにしてそのような互換性を提供しますか?すべてのビルドツールを利用できます。

私にとっては、高水準言語(Java)を低水準言語(バイトコード)に変換するだけで簡単に思えます。高水準言語が変わっても、低水準言語は変わらないはずです。だからこそ可能だと思った。

[〜#〜]更新[〜#〜]

みんな、この答えは重複しないと思います OpenJDK-11に移動しますが、Java 8 でコンパイルします。なぜなら、OPはコードを生成し続ける方法を尋ねるからです。 Java 8の機能ですが、Java 11をターゲットにしています(これは、有名な下位互換性です)。私の質問はその逆です。Java 11でコードを生成したいのですが、Java 8をターゲットにしたいと思います。質問する前にトピックを調査していたときに、その質問に出くわしました。私の状況に適用できるとは思いませんでした。

他の質問 Java 8コードをコンパイルしてJava 7 JVM で実行できるかどうかは私の質問と似ていますが、2013年に尋ねられました。 Java 7とJava 8の間でバイトコードが明らかに変更されました。

Java 8以来、バイトコードはそれほど変わっていないと思いました。そのため、この質問をしました。

22
neshkeev

JDK 11向けにコンパイルされたクラスをJDK 8に変換することは、理論的には高度なツールで可能ですが、簡単ではありません。バイナリレベルで重要な変更があります。

最初に、JDK 11は nest タイプを導入しました。これにより、内部/外部クラスのprivateメンバーにアクセスするときに合成アクセサーメソッドを生成する必要がなくなります。もちろん、そのようなアクセスは古いバージョンでは失敗します。

また、 動的定数 も導入しましたが、Java言語がその機能をどこで利用しているかはわかりません。これは主に将来のバージョンを対象としています。

次に、JDK 9以降、文字列連結はinvokedynamicを使用してコンパイルされます Java.lang.invoke.StringConcatFactory Java 8には存在しません。

機能する可能性のある機能は、インターフェースのprivateメソッドであり、Java 9で言語機能として導入されましたが、Java 8ではすでにバイナリレベルで処理されていました。

Java 8もモジュール定義を処理できませんが、おそらく無視されます。

13
Holger

Javacのjavadocで明示的なものは何もありませんでしたが、-sourceオプションと-targetオプションの両方に同じバージョンしか指定できないと思います。 Java 11の機能はJava 8ではサポートされていませんが、その逆は当てはまりますが、より高いJavaバージョンはコンパイルされたコードを実行できます以前のバージョンでは、Java 11で書かれたコードをコンパイルしてJava 8.で実行できるようにすることはできません。

6
Abra

私はここで間違っている可能性がありますが、少なくとも今のところ、javacはそのように使用するためのものではありません。

ここで少し推測してみましょう:--release 8 --target 8は機能します(--source 11パラメータ)。

しかし、これがうまくいくとは思えません。 javacにはN個のソースコード機能を受け入れるサポートがなく、以前のターゲットバージョンに逆コンパイルされたと思います。

もちろん、コンパイラーは、N個のソースコードを(N-m)バイトコードに変換するために必要な変換についての知識を持つことができます。しかし、それはコンパイラーをはるかに複雑にし、各リリースはそれに追加されます。 testingの取り組みに劇的なコストも追加されます。コンパイラのメンテナがそれに喜んで同意することはないでしょう。これは広く使用されているユースケースではないようです。

だから、のみ "解決策"は知っています:分岐と二重メンテナンス。そして、物事を合理的に保つために、私は単にJava 8バージョン、およびたぶん Java 11。

いいえ、Java 11ソースをJava 8バイナリにコンパイルすることはできません。

javacの用語では、-sourceパラメータを-targetパラメータより大きくすることはできません。

したがって、Java 8バイナリを生成する場合、ソースはJava 8(またはそれ以前)で作成する必要があります。何も使用しない場合= Java 11言語機能。ソースは基本的にすでにJava 8なので、これはあまり大きな問題にはなりません。

JDK 11を使用してJava 8ソースをJava 8バイナリにコンパイルできます。JDKバージョン) canは、ソースおよび/またはターゲットのバージョンよりも大きいことができます。

注:javacドキュメントでは、-sourceパラメータを-targetパラメータ以下にする必要があることについては何も述べられていません。ただし、非公式のドキュメントはたくさんあります。たとえば、 https://stackoverflow.com/a/9261298/691074

私が知る限り、実際にそのような状況が機能するようにする単一の反例もありません。

5
Buurman

@ bsideup によって作成された Jabel と呼ばれるツールがあり、これを行うことができます。これは、javacにプラグインできるように、注釈プロセッサのふりをします。ただし、実際の注釈処理は行いません。代わりに、コンパイル中にjavac内部にハッキングし、 var のような新しい機能、匿名クラス作成のダイヤモンド、そして テキストブロック や-のような新しい機能を信じ込ませます。 スイッチ式 はJava 8.で使用できます。

これは保証のないハックなソリューションなので、注意して使用してください。

3
Tagir Valeev