Java 9の準備。最も興味深い改善点のレビュー







今週の 8月30日水曜日、 JUG.ruとZeroTurnaroundの開発者であり、JRebelとXRebelを開発するOleg Shelaevの 会議が Oracleオフィスで開催されました。 会議のトピックは、マルチスレッドJavaプログラムを作成するためのツールでした(自転車の設計からハンドル付きスレッドの開始、ForkJoinPool、グリーンスレッド、トランザクションメモリまで)。







もちろん、ZeroTurnaroundで最も有用と考えられるJava 9チップを尋ねました。 その結果、私たちはあなたが今読んでいる記事を手に入れました。 Oleg はRebelLabsブログに元の記事公開しましたが、もっと面白いことがあります。







それで、始めました。







私たちは長い間Java 9を待っていましたが、今ではリリースが間近に迫っています。 やった! それは最も簡単な方法ではありませんでしたが、それにもかかわらず、モジュールシステムについての最も熱い議論でさえゆっくりと前進しており、ほとんどの参加者は同意します。 最も可能性が高いのは、たとえば2017年9月21日にリリースが間もなく行われることです。







この投稿では、モジュールシステムの詳細な説明については触れません。 代わりに、すべての開発者が使用できること、つまりJava言語の革新と標準APIについて説明します。







以下は、Java 9 APIにおける私たちのお気に入りの革新のリストです。 原則として、本質を理解するには、コード例を見てください。 ただし、JShellを実行し、これらすべての例を個別に実行して、自分の目で結果を確認することができます。







JShellを起動します、お待ちください...準備はできましたか? まだ? わかった...終わった? まだ? はい、暑くなるには時間がかかります... OK、始まりました、素晴らしい! 始めます。







コレクションのファクトリメソッド



Java 9で最も期待される機能の1つは、コレクションリテラルを作成して、最も簡単なケースをより便利に記録できることです。 もちろん冗談です。Javaについて話しているのですが、リテラルはなく、静的なファクトリメソッドしかありません。 ただし、既製のメソッドを使用してList



Map



、およびSet



を簡単に作成できます。







 jshell> List.of(1, 2, 3) $1 ==> [1, 2, 3] jshell> Set.of(1, 2) $2 ==> [2, 1] jshell> Map.of("hello", "world") $3 ==> {hello=world}
      
      





これは、 List



Map



、およびSet



が使用方法を学習したインターフェース(Java 8)の静的メソッドの出現により可能になりました。







その結果、パフォーマンスが最大になるように最適化された取り外し不可能なコレクションが作成されます。 たとえば、 List1



は値をクラスフィールドに格納し、アクセスを高速化します。







 jshell> List.of(1).getClass() $4 ==> class java.util.ImmutableCollections$List1
      
      





ストリーム



Stream APIには、非常に便利な機能がいくつか追加されています。 特に、 dropWhile



およびtakeWhile



。 名前が示すように、 dropWhile



は条件が満たされるまで要素を最初から破棄し、 dropWhile



は条件が満たされるまで要素をtakeWhile



ます。







 jshell> IntStream.range(1, 10).dropWhile(x -> x < 5).forEach(System.out::println) 5 6 7 8 9
      
      





次の便利な追加はiterate()



メソッドです。 サイクルをストリームに置き換えることができます。 ストリームの初期値、条件(反復を停止するタイミング)、および遷移関数(正確に次の要素を取得する方法)を渡す必要があります。







 jshell> IntStream.iterate(0, x -> x < 3, x -> x + 1).forEach(System.out::println) 0 1 2
      
      





ストリームで不動点計算を突然行いたい場合、この夢はJava 9で実現できます。







オプショナル



突然誰かがそれらを使用する方法を覚えていない場合、私たちは優れたチートシートを持ってます。 Java 9では、 or()



メソッドが最終的に追加されました。これにより、 isPresent()



での一定のチェックをisPresent()



ことなく、異なるOptional'yを1行でバインドできます。







 public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) jshell> Optional.empty().or(() -> Optional.of("RebelLabs")) $5 ==> Optional[RebelLabs]
      
      





次の優れた追加機能は、Optionalを1つ以下の要素を含むストリームに変換する機能です。 これは、遅延ストリームを使用する場合に非常に便利です。 以下の例では、自分の目で違いを見ることができます。 オプションでmap()



を呼び出すと、マッピングは即座に行われますが、ストリームでは行われません。







 jshell> Optional.of(1).map(x -> x * 3) $10 ==> Optional[3] jshell> Optional.of(1).stream().map(x -> x * 3) $11 ==> java.util.stream.ReferencePipeline$3@67b92f0a
      
      





最後に、 ifPresentOrElse



メソッドがあります。 Java 8では、Optional値が存在する場合の動作のみを定義できました。 Java 9では、値が存在する場合と存在しない場合の対処方法を決定する2つの異なるRunnableを指定することが可能になりました。







 jshell> Optional.empty().ifPresentOrElse(x -> System.out.println(x), () -> System.out.println("empty")); empty
      
      





完全なテーブルの未来



洗練されたもう1つのAPIは、 CompletableFuture



クラスです。 いくつかの優れた機能が追加され、さらに正確なマルチスレッドコードを記述できるようになりました。







最もクールなメソッドの1つはcopy()



、このCompletableFuture



なコピーを返します。 次の例では、 CompletableFuture



を作成し、そのコピーを作成し、コピーの完了が元のオブジェクトに影響しないことを確認します。 これは、 CompletableFuture



を返す非同期APIを作成するときに非常に便利です。 以前は、クライアント自身がそのようなAPIから返されたCompletableFuture



を完了することができる状況を回避して、当然のことながら自分自身を苦しめる必要がありました。 今のところ、 copy()



メソッドを呼び出すだけで十分です。







 jshell> CompletableFuture<String> future = new CompletableFuture<>() future ==> java.util.concurrent.CompletableFuture@35d176f7[Not completed] jshell> future.copy() $15 ==> java.util.concurrent.CompletableFuture@4973813a[Not completed] jshell> future.isDone() $17 ==> false jshell> $15.isDone() $18 ==> false jshell> $15.complete("JRebel") $19 ==> true jshell> $15.isDone() $20 ==> true jshell> future.isDone() $21 ==> false
      
      





しかし、最もクールなことは、親の停止がすべてのコピーに適用されることです!







 jshell> CompletableFuture<String> future = new CompletableFuture<>() future ==> java.util.concurrent.CompletableFuture@4bbfb90a[Not completed] jshell> future.copy() $24 ==> java.util.concurrent.CompletableFuture@5a8806ef[Not completed] jshell> future.complete("XRebel") $25 ==> true jshell> $24.isDone() $26 ==> true
      
      





さらに、彼らは最終的にタイムアウトを追加しました。 組み込みのタイムアウト関数を使用せずに非同期APIを操作すると、非常にストレスがたまります。 Java 9では、手動で設定された時間が経過した後、 CompletableFuture



を完了する方法を正確に決定することが可能になりました。







 jshell> CompletableFuture<String> future = new CompletableFuture<>() future ==> java.util.concurrent.CompletableFuture@67205a84[Not completed] jshell> future.completeOnTimeout("Isn't this amazing", 1, TimeUnit.SECONDS) $28 ==> java.util.concurrent.CompletableFuture@67205a84[Not completed, 1 dependents] jshell> future.isDone() $29 ==> true
      
      





プロセス管理API



Java 9より前は、プロセス制御は、私たちが信じたいほどクロスプラットフォームとして作られていませんでした。 サブプロセスの操作は少し曲がりくねっていましたが、現在はJava 9で最終的に修正されました。 Java 9はProcessHandle



クラスを追加します。 ProcessHandle



クラスは、現在のプロセス、フィードで見つかった他のプロセス、それらの子プロセスなどを分析するためのAPIを提供します。 例を見てください:







 jshell> ProcessHandle current = ProcessHandle.current(); current ==> 6349 jshell> current.pid() $33 ==> 6349 jshell> current.info().\TAB arguments() command() commandLine() equals( getClass() hashCode() notify() notifyAll() startInstant() toString() totalCpuDuration() user() wait( jshell> current.info().command() $34 ==> Optional[/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/bin/java]
      
      





最も頻繁に使用される関数は、プロセスを開始するために使用されるコマンドラインと引数を取得することですが、他の機能も注目に値します。







Java 9でより便利に行えるもう1つの一般的なタスクは、プロセスが完了した直後にコードを実行することです。 Java 9では、このために新しいメソッドを使用することをお勧めします。







 CompletableFuture<Process> onExit()
      
      





何をしたいかを示してください。 これ以上の涙や不安定な外国図書館はありません。







Stackwalker



喜ぶ、例外を嫌う! これで、 Exception



オブジェクトを作成せずにスタックトレースを操作できます。 StackWalker



へようこそ!







StackWalker



すると、スタックのローミング、フィルタリング、および他のさまざまなことを効果的に行うことができます。 この例では、スタックトレースから上位5つの要素を取得します。







 jshell> StackWalker.getInstance().walk(s -> s.limit(5).collect(Collectors.toList())); $36 ==> [do_it$(java:36), jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:209), jdk.jshell/jdk.jshell.execution.RemoteExecutionControl.invoke(RemoteExecutionControl.java:116), jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:119), jdk.jshell/jdk.jshell.execution.ExecutionControlForwarder.processCommand(ExecutionControlForwarder.java:134)]
      
      





Javaの改善



APIだけでなく、言語自体も改善されています。 まず、 _



(アンダースコア)文字は有効な識別子ではなくなりました。 何らかの理由で使用する場合は、二重下線に切り替える必要があります! (ヒント:これをしないでください)。







 jshell> int _ = 1 | Error: | as of release 9, '_' is a keyword, and may not be used as an identifier | int _ = 1 | ^ | Error: | reached end of file while parsing | int _ = 1 | ^ jshell> int __ = 1; __ ==> 1
      
      





これは、将来、関数を呼び出すときに不要な(オプションの)パラメーターをアンダースコアに置き換えることができるようにするためです。







インターフェイスも若干変更されています。 Java 9のインターフェイスには、プライベートメソッドを含めることができます。 Java 8では、いくつかの一般的なロジックをデフォルトのメソッドに保存できました。 これで、補助クラスを作成することなく、インターフェイス内の一般的なロジックを分離できます。







次に、小さな合成例を示します。







 jshell> interface A { private int zero() { return 0;} default int one() { return zero() + 1;}} | created interface A
      
      





そして最後に、最後の革新。 これで、try-with-resourcesブロックで最終変数を効果的に使用できます。 これによりコードが簡素化され、 try



内で変数を宣言する必要がなくなりました。 try



ブロックでそれらを操作するだけで、これがコンパイルされます。







 boolean a() throws Exception { Socket s = new Socket(); try (s) { } return s.isClosed(); }
      
      





try



ブロックの実行後、そこAutoClosable



記載されているすべてのAutoClosable



自然に閉じます。







おわりに



やった! 私たちはあらゆるものを調べましたが、これはJava 9に登場したすべての革新ではありません。それでも、上記のものは私たちにとって最も有用であるように思われ、できるだけ早く使用されます。







Java 9のリリースはかなり前から行われており、私たち全員がそれが私たちにどのように影響するかを理解する時です。 clasthpathの操作モードは変更されないままであり、Java 9への移行は簡単で痛みがないため、これまで以上に関連性があります。 完成したJava 9のビルドを今すぐダウンロードし、新しいAPIを整理して試してみて、今後の明るい未来に備えましょう!







著者



Oleg ShelaevはJava開発者であり、ZeroTurnaroundの開発者支持者です。 Javaエージェントやテストを書いていないときは、RebelLabsブログに書き込んだり、会議で講演したりします。 余暇には、タルトゥ大学で科学の推進を試み、動的なソフトウェア更新の問題を研究しています。








All Articles