Java 10移行ノート

こんにちは、Habr。 Java 10の公式リリースが最近行わましたが、ほとんどの人が主に8区を使用していることを考えると、10のリリースで、モジュール性(9区に含まれる)やローカル変数型推論などの機能を待っています 。 いいですね、既存のプロジェクトを10区に移してみてください。



画像






カットの下で私たちを待っている痛みの種類を知ることができます。



Javaプロジェクトを8番目のバージョンから9番目または10番目に移行する際に発生する可能性のあるすべての問題をここに記載するつもりはありません。 同僚との口頭での話し合い以外の何らかの形で、Javaの新しいバージョンとの最初のコンタクトの小さな経験を修正したいと思います。 たぶん彼誰かを止めるだろう、誰かが役に立つだろう。

画像








まず、IDEAバージョン2018.1は、JVMバージョン9および10と2つの異なるマシンでの動作を拒否しました。 GUI要素の一部がなく、残りの要素の一部にテーマがないように見えました。 IDEAは実行する特定のJVMを指定できるため、これは小さな問題でした。



1.モジュール性の実装に関する問題



始めるために、アプリケーションをモジュール化しましょう(JDK開発者がJigsawプロジェクトを試してみて苦しんだのは、何のためでもありませんでした)?



1.1。 jdepsユーティリティの問題



既存のクラス階層からjavaモジュールを取得するには、次のものが必要です





画像






一見、すべてが非常に単純なようです。



1.1.1。 外部ライブラリの依存関係は、特別なスペルなしでは機能しなくなりました



ただ一緒になるものは何もありません。 モジュラーアプリケーションを構築する場合、依存関係もモジュラーでなければなりません。 そうしないと、単にmodule-info.javaに追加できません。 より正確には、追加できます-ライブラリの昔ながらのjarファイルは、いわゆる「自動モジュール」と見なされます。 ただし、問題は、アセンブリ中に自動モジュールを使用できないことです。 はい、これまで人気のあるリポジトリにモジュールとしてコンパイルされたライブラリは非常に少なく、使用することはできません。 誰も気にしません。



これはjdepsユーティリティが必要だった場所です。jdepsユーティリティはjarファイルを解析し、古いファイル形式のjarからモジュールを作成するためのmodule-info.javaを生成できます。 回避策として、すべての依存関係を1つのヒープに展開し、すべての依存関係を持つファッショナブルなモジュールを作成できる単一のjarを作成することが決定されました。 stackoverflowの例が添付されています。



1.1.2。 実際に必要な依存関係よりもはるかに多くの依存関係を含める必要があります



Jdepsは依存関係を再帰的に解決し、Maven / Gradleがオプションと見なす偶数の依存関係の必須の解決を必要とします。 その結果、小さな実験プロジェクトの依存関係ツリーも数回成長します。 パラグラフ(1.1.1)で製造することが決定された依存関係のあるモジュールは、インターネットのフロアを非常に大きくします。 ここにこの痛みのプレゼンテーションの例。 突然、log4jにはAWT、OSGI、ZeroMQ、Kafkaなどが必要になるようです。



1.1.3。 予期しないパブリック[Roskomnadzor]ユーティリティ



いくつかのクラスが異なるモジュールに複製されている状況では、jdepsはコードの深さのどこかからIllegalStateExceptionスタックレースで抜け出し、何が問題なのかを説明しませんでした。 1つの小さな手がかりは、例外のコンストラクターへの引数としてのメッセージ「自己依存」です。 ある時点で、この不名誉 、すべて(アプリケーションクラス、依存関係クラス、「オプション」の依存関係のクラス)を1つのjarファイルにダンプする必要があるという事実を暗示しているように思えました 。 そして本当に-それは働いた。



1.2。 ランタイムの問題



すべてがすでに奇跡的に組み立てられていたとしても、モジュール式アプリケーションと、そのためのカスタムJREイメージでさえ、最も重要な驚きがまだ来ていないことが判明しました。 つまり、実行時です。



1.2.1。 これで、開始ファイル/ディレクトリへのパスを決定することは不可能です



Javaアプリケーションでは、多くの場合、キジが置かれているディレクトリへのパス、アプリケーションが起動されたjarファイル(エントリポイントを含むjarファイルなど)を決定する必要があります。 これは通常、アプリケーションファイルを探す場所(構成、プラグインなど)を理解するために必要です。 そのようなものは、原則として、隣にあります(非常に論理的です)。 フォームの呪文は通常、このパスを決定するのに役立ちます。



Main.getProtectionDomain().getCodeSource().getLocation().toURI()
      
      





Javaの新しいバージョンではこの呪文が機能しなくなり、ファイルパスの代わりに「jrt://com.example.myapp」のようなものが表示されることを発見したときの驚きを想像してください。 私は熱心にグーグルで探し始め、スタックをオーバーフローさせました。 モジュールを特定し、どのファイルがそのモジュールに属しているのかを何らかの方法で把握しようとしました。 クラスパスにアクセスしようとしても、私は何もしませんでした。アプリケーションは、昔ながらのクラスパスではなく、いわゆるモジュールパスでモジュール式に起動されるからです。 フィニッシュでした。 その瞬間、Java 10に移行する際の実験を最小限に抑えることにしました。もちろん、コマンドラインでディレクトリにパスを渡すことも、正しいディレクトリからのみユーザーを強制的に実行することもできます。 しかし、私自身は、今のところ十分であると個人的に決めました。



1.2.2。 ファイルのMIMEタイプを判別しようとしたときの失敗。



ケーキの上のチェリーは一人ではないことが判明しました。 トップ10での実験のために、それをインストールし、システムのデフォルトJVMとして構成しました。 したがって、驚きは続きました。 のようなコード



 final String mimeType = Files.probeContentType(itemInputFilePath);
      
      





引数が対応する拡張子を持つ通常のCSVファイルである場合、nullが返されるという事実につながり始めました。 「どのように?」と思ったのですが、「JVMは通常のCSVファイルのMIMEタイプを判別できませんか?」 私は少し物beいでした



 Files.readAllLines("/etc/mime.types", Charset.defaultCharset())
      
      





「不正な入力」のようなメッセージで小さな例外をスローします。

この不名誉の理由については、私はまだ始めていませんでした。 /etc/mime.typesファイルを確認しました-krakozyabrasが見つかりませんでした 。通常のテキストファイルのようです。 つまり、JVMはテキストファイルから行を読み取ることができませんでした。



1.2.3。 壊れた新しいサービスプロバイダーメカニズム



バージョン9以降、Javaは、古いサービスプロバイダーを置き換える新しいサービスプロバイダーメカニズムも提供します。 悪いことではなく、実行時に許可されるプラグインを作成できます。 新しいメカニズムでは、キーワード「provides」および「with」を使用してmodule-info.javaで拡張子を宣言します。 それは簡単かもしれません。 実際には、古いメカニズム(リソース/ META-INF / services / ...ファイルを使用)とは異なり、これは機能しませんでした。 時間の問題が十分ではなかったことを理解してください。 しかし、特別な呪文なしではうまくいかないというマークは、おそらくここでやるでしょう。



2.ローカル変数型推論の実装に関する考察



ローカル変数型推論の場合、Scalaの影響を示唆する新しいキーワード「var」(変数、変数)があります。 しかし、個人的には「val」(値)も追加しなかった理由は個人的には奇妙に思えました。 代わりに、「final var」を記述する必要があります。これは「final variable」または「constant variable」として目で読み取られます-これは簡潔さだけでなく、私の意見ではよく見られるように多少矛盾しています。



かなり面白いものもいくつか登場しました。 例:



 final var someStrings = new ArrayList<String>();
      
      





ここで、JVMは「someStrings」という名前のArrayList <String>を「記憶」しています。 時々、 満月の巧妙な構文の強調表示で、定数のタイプが冗長であるという警告が表示されることがあります。Javaでは、この場所でList <String>やCollection <String>などのインターフェースを使用するのが一般的です。 非常に巧妙なエディターからのこの警告は、次のような定数を宣言することで回避できます:潜在的な接続を減らすために最小限必要なインターフェイスを使用するのが一般的であるため、このタイプは冗長です(そのため、たとえば、だれもArrayList固有のメソッドを取得しません)。 ローカル変数型の推論はこの原則に違反します。 これは次の方法で回避できます。



 final var someStrings = (List<String>) new ArrayList<>();
      
      





しかし、すでにどういうわけか非常に簡潔ではないように見えます。 この瞬間は、いくつかの致命的な欠陥のタイトルのふりをするものではありませんが、わずかな一貫性のなさを示しています。



3.残念な結論



このノートで表明された問題のいくつかは、9番目のバージョンで発生しました。 それらが今まで修正されていないことを考えると、この混乱全体が第11バージョン以降にあるという不快感があります。 私はクリックしたくありませんが、この主観的な感覚から何かが続きます。プログラミング言語としてのJavaは衰退を経験し始めます。 たぶん私は間違っているかもしれませんが、多分そうです。Javaを置き換えるには、別のツールを探す必要があります。



更新



残念ながら、私はメモリから書き込みますので、いくつかの詳細が欠落しています。 私はまた別の素晴らしいことを思い出しました。 これはannotashka sun.misc.Contendedで、8番目のバージョンに登場し、その後のバージョンでは安全に飲まれました。 彼女の短い人生にもかかわらず、彼女は使用されたライブラリの 1つに入ることができましたが、これも多くのトラブルを引き起こしました。 それは10-keではなく、あなたが望むことをしますが、クラスへのリンクがあるため、モジュールを構築するために必要です。 それはすべて、conversantmediaディスラプターライブラリが必須の依存関係ではないことを思い出して、ソースの空のクラスに必要なクラスを置き換えるだけでした。 そして、これは数がなかった小さなものの一つです...



All Articles