Javaプラットフォームの最新バージョン、 Java 8 、1年以上前にリリースされました。多くの企業と 開発者 あるプラットフォームバージョンから別のプラットフォームバージョンへの移行には多くの問題があるため、これは理解できる以前のバージョンで引き続き機能します。それでも、多くの開発者はまだ始めています 新着 古いバージョンのJavaを使用するアプリケーション。 Java 8によって言語にいくつかの重要な改善がもたらされたため、これを行う正当な理由はほとんどありません。
沢山あります 新機能 Java8で。最も便利で興味深いものをいくつか紹介します。
CompletableFuture
を使用した非同期タスクチェーンに ラムダ は、1回以上実行するために参照して、別のコードに渡すことができるコードブロックです。たとえば、他の言語の無名関数はラムダです。関数と同様に、ラムダは実行時に引数を渡して、結果を変更できます。 Java8が導入されました ラムダ式 、ラムダを作成して使用するための簡単な構文を提供します。
これによりコードがどのように改善されるかの例を見てみましょう。ここに、2つのInteger
を比較する単純なコンパレータがあります。 2を法とする値
class BinaryComparator implements Comparator{ @Override public int compare(Integer i1, Integer i2) { return i1 % 2 - i2 % 2; } }
このクラスのインスタンスは、将来、次のように、このコンパレータが必要なコードで呼び出される可能性があります。
... List list = ...; Comparator comparator = new BinaryComparator(); Collections.sort(list, comparator); ...
新しいラムダ構文により、これをより簡単に行うことができます。これは、compare
と同じことを行う単純なラムダ式です。 BinaryComparator
からのメソッド:
(Integer i1, Integer i2) -> i1 % 2 - i2 % 2;
構造には、関数と多くの類似点があります。括弧内に、引数のリストを設定します。構文->
これがラムダであることを示しています。そして、この式の右側に、ラムダの動作を設定します。
これで、前の例を改善できます。
... List list = ...; Collections.sort(list, (Integer i1, Integer i2) -> i1 % 2 - i2 % 2); ...
このオブジェクトで変数を定義できます。それがどのように見えるか見てみましょう:
モンテカルロシミュレーションの構築方法
Comparator comparator = (Integer i1, Integer i2) -> i1 % 2 - i2 % 2;
これで、次のようにこの機能を再利用できます。
... List list1 = ...; List list2 = ...; Collections.sort(list1, comparator); Collections.sort(list2, comparator); ...
これらの例では、ラムダがsort()
に渡されていることに注意してください。 BinaryComparator
のインスタンスと同じ方法でメソッド前の例で渡されます。 JVMはラムダを正しく解釈することをどのように知っていますか?
関数がラムダを引数として使用できるようにするために、Java8では新しい概念が導入されています。 機能インターフェイス 。関数型インターフェースは、抽象メソッドが1つしかないインターフェースです。実際、Java 8は、ラムダ式を関数型インターフェースの特別な実装として扱います。つまり、ラムダをメソッド引数として受け取るには、その引数の宣言された型が関数型インターフェースである必要があるだけです。
関数型インターフェースを宣言するときに、@FunctionalInterface
を追加できます。開発者にそれが何であるかを示す表記法:
@FunctionalInterface private interface DTOSender { void send(String accountId, DTO dto); } void sendDTO(BisnessModel object, DTOSender dtoSender) { //some logic for sending... ... dtoSender.send(id, dto); ... }
これで、メソッドsendDTO
を呼び出して、次のようにさまざまなラムダを渡してさまざまな動作を実現できます。
sendDTO(object, ((accountId, dto) -> sendToAndroid(accountId, dto))); sendDTO(object, ((accountId, dto) -> sendToIos(accountId, dto)));
Lambda引数を使用すると、関数またはメソッドの動作を変更できます。最後の例でわかるように、ラムダが別のメソッド(sendToAndroid
またはsendToIos
)を呼び出すためだけに機能する場合があります。この特別な場合のために、Java8は便利な省略形を導入します。 メソッドリファレンス 。この省略された構文は、メソッドを呼び出すラムダを表し、形式はobjectName::methodName
です。これにより、前の例をさらに簡潔で読みやすくすることができます。
sendDTO(object, this::sendToAndroid); sendDTO(object, this::sendToIos);
この場合、メソッドsendToAndroid
およびsendToIos
this
に実装されていますクラス。また、別のオブジェクトまたはクラスのメソッドを参照する場合もあります。
Java 8は、まったく新しいStream APIの形で、Collections
と連携する新しい機能をもたらします。この新機能はjava.util.stream
によって提供されますパッケージ、およびより多くを可能にすることを目的としています 機能的 コレクションを使ったプログラミングへのアプローチ。これから説明するように、これは主に、先ほど説明した新しいラムダ構文のおかげで可能です。
Stream APIは、コレクションの簡単なフィルタリング、カウント、およびマッピングに加えて、コレクションから情報のスライスとサブセットを取得するさまざまな方法を提供します。機能スタイルの構文のおかげで、Stream APIを使用すると、コレクションを操作するためのより短く、より洗練されたコードが可能になります。
短い例から始めましょう。すべての例でこのデータモデルを使用します。
class Author { String name; int countOfBooks; } class Book { String name; int year; Author author; }
すべての著者をbooks
で印刷する必要があると想像してみましょう。 2005年以降に本を書いたコレクション。Java7でどのように行うのでしょうか。
for (Book book : books) { if (book.author != null && book.year > 2005){ System.out.println(book.author.name); } }
そして、Java 8でそれをどのように行うのでしょうか?
books.stream() .filter(book -> book.year > 2005) // filter out books published in or before 2005 .map(Book::getAuthor) // get the list of authors for the remaining books .filter(Objects::nonNull) // remove null authors from the list .map(Author::getName) // get the list of names for the remaining authors .forEach(System.out::println); // print the value of each remaining element
たった一つの表現です!メソッドの呼び出しstream()
任意のCollection
Stream
を返しますそのコレクションのすべての要素をカプセル化するオブジェクト。これは、filter()
などのStreamAPIのさまざまな修飾子を使用して操作できます。およびmap()
。各修飾子は新しいStream
を返しますさらに操作できる変更の結果を含むオブジェクト。 .forEach()
メソッドを使用すると、結果のストリームのインスタンスごとに何らかのアクションを実行できます。
この例は、関数型プログラミングとラムダ式の間の密接な関係も示しています。ストリーム内の各メソッドに渡される引数は、カスタムラムダまたはメソッド参照のいずれかであることに注意してください。技術的には、前のセクションで説明したように、各修飾子は任意の機能インターフェイスを受け取ることができます。
Stream APIは、開発者がJavaコレクションを新しい角度から見るのに役立ちます。 Map
を取得する必要があると想像してください。各国で利用可能な言語の。これはJava7でどのように実装されますか?
Map countryToSetOfLanguages = new HashMap(); for (Locale locale : Locale.getAvailableLocales()){ String country = locale.getDisplayCountry(); if (!countryToSetOfLanguages.containsKey(country)){ countryToSetOfLanguages.put(country, new HashSet()); } countryToSetOfLanguages.get(country).add(locale.getDisplayLanguage()); }
Java 8では、物事は少しすっきりしています。
import java.util.stream.*; import static java.util.stream.Collectors.*; ... Map countryToSetOfLanguages = Stream.of(Locale.getAvailableLocales()) .collect(groupingBy(Locale::getDisplayCountry, mapping(Locale::getDisplayLanguage, toSet())));
メソッド collect()
さまざまな方法でストリームの結果を収集できます。ここでは、最初に国ごとにグループ化し、次に各グループを言語ごとにマップしていることがわかります。 (groupingBy()
とtoSet()
はどちらもCollectors
クラスの静的メソッドです。)
外国為替リスクを管理するために取られる措置は次のうちどれですか?
StreamAPIには他にも多くの機能があります。完全なドキュメントは見つけることができます ここに 。このパッケージが提供するすべての強力なツールをより深く理解するために、さらに読むことをお勧めします。
CompletableFuture
を使用した非同期タスクチェーンJava7の場合 java.util.concurrent
パッケージ、インターフェースがあります Future
、これにより、将来の非同期タスクのステータスまたは結果を取得できます。この機能を使用するには、次のことを行う必要があります。
ExecutorService
、非同期タスクの実行を管理し、Future
を生成できます。進行状況を追跡するオブジェクト。Runnable
仕事。ExecutorService
でタスクを実行すると、Future
が提供されます。ステータスまたは結果へのアクセスを提供します。非同期タスクの結果を利用するには、Future
の方法を使用して外部から進行状況を監視する必要があります。インターフェイス、および準備ができたら、明示的に結果を取得し、それらを使用してさらにアクションを実行します。これは、特に同時タスクが多数あるアプリケーションでは、エラーなしで実装するのはかなり複雑になる可能性があります。
ただし、Java 8では、Future
コンセプトはさらに進められ、 CompletableFuture
非同期タスクのチェーンの作成と実行を可能にするインターフェース。これは、Java 8で非同期アプリケーションを作成するための強力なメカニズムです。これにより、完了時に各タスクの結果を自動的に処理できるようになります。
例を見てみましょう:
import java.util.concurrent.CompletableFuture; ... CompletableFuture voidCompletableFuture = CompletableFuture.supplyAsync(() -> blockingReadPage()) .thenApply(this::getLinks) .thenAccept(System.out::println);
メソッドCompletableFuture.supplyAsync
デフォルトで新しい非同期タスクを作成しますExecutor
(通常は ForkJoinPool
)。タスクが終了すると、その結果は関数this::getLinks
への引数として自動的に提供されます。この関数は、新しい非同期タスクでも実行されます。最後に、この第2段階の結果は自動的にSystem.out
に出力されます。 thenApply()
およびthenAccept()
いくつかのうちの2つです 便利な方法 Executors
を手動で使用せずに並行タスクを構築するのに役立ちます。
CompletableFuture
複雑な非同期操作のシーケンスを簡単に管理できます。 3つのタスクでマルチステップの数学演算を作成する必要があるとします。 タスク1 そして タスク2 さまざまなアルゴリズムを使用して最初のステップの結果を見つけます。そのうちの1つだけが機能し、もう1つは失敗することがわかっています。ただし、どちらが機能するかは、事前にわからない入力データによって異なります。これらのタスクの結果は、次の結果と合計する必要があります。 タスク3 。したがって、どちらかの結果を見つける必要があります タスク1 または タスク2 、 そして 結果として タスク3 。これを実現するために、次のように書くことができます。
import static java.util.concurrent.CompletableFuture.*; ... Supplier task1 = (...) -> { ... // some complex calculation return 1; // example result }; Supplier task2 = (...) -> { ... // some complex calculation throw new RuntimeException(); // example exception }; Supplier task3 = (...) -> { ... // some complex calculation return 3; // example result }; supplyAsync(task1) // run task1 .applyToEither( // use whichever result is ready first, result of task1 or supplyAsync(task2), // result of task2 (Integer i) -> i) // return result as-is .thenCombine( // combine result supplyAsync(task3), // with result of task3 Integer::sum) // using summation .thenAccept(System.out::println); // print final result after execution
Java 8がこれをどのように処理するかを調べると、3つのタスクすべてが同時に非同期に実行されることがわかります。にもかかわらず タスク2 例外を除いて失敗すると、最終結果が計算され、正常に出力されます。
CompletableFuture
複数のステージを持つ非同期タスクの構築がはるかに簡単になり、各ステージの完了時に実行するアクションを正確に定義するための簡単なインターフェイスが提供されます。
Javaが述べているように 自分の入場 :
Java SE 8リリースの前は、Javaの日付と時刻のメカニズムは
java.util.Date
、java.util.Calendar
、およびjava.util.TimeZone
クラス、およびjava.util.GregorianCalendar
などのサブクラス。これらのクラスには、次のようないくつかの欠点がありました。
- Calendarクラスはタイプセーフではありませんでした。
- クラスは変更可能であるため、マルチスレッドアプリケーションでは使用できませんでした。
- 異常な月数と型安全性の欠如により、アプリケーションコードのバグは一般的でした。」
Java 8はついに、これらの長年の問題を新しい java.time
パッケージ。日付と時刻を操作するためのクラスが含まれています。それらはすべて不変であり、一般的なフレームワークと同様のAPIを備えています ジョーダタイム 、ほとんどすべてのJava開発者が、ネイティブのDate
、Calendar
、およびTimeZone
の代わりにアプリケーションで使用します。
このパッケージの便利なクラスのいくつかを次に示します。
Clock
-現在の時刻、日付、タイムゾーン付きの時刻など、現在の時刻を知らせる時計。Duration
、および Period
-時間。 Duration
「76.8秒」などの時間ベースの値、および「4年、6か月、12日」などの日付ベースの値を使用します。Period
-いくつかの形式の瞬間的な時点。Instant
、 LocalDate
、 LocalDateTime
、 LocalTime
、 Year
-ISO-8601カレンダーシステムにタイムゾーンがない、日付、時刻、年、月、またはそれらの組み合わせ。YearMonth
、 OffsetDateTime
-「2015-08-29T14:15:30 + 01:00」など、ISO-8601カレンダーシステムのUTC /グリニッジからオフセットされた日時。OffsetTime
-「1986-08-29T10:15:30 + 01:00ヨーロッパ/パリ」など、ISO-8601カレンダーシステムに関連付けられたタイムゾーンを持つ日時。
勘定科目表のベストプラクティス
「月の第1火曜日」などの相対的な日付を見つける必要がある場合があります。これらの場合ZonedDateTime
特別なクラスを提供します java.time
。 TemporalAdjuster
クラスには、静的メソッドとして使用できるアジャスターの標準セットが含まれています。これらにより、次のことが可能になります。
月の最初の火曜日を取得する方法の簡単な例を次に示します。
TemporalAdjuster
まだJava7を使用していますか?プログラムに参加しましょう! #Java8 つぶやき ご覧のとおり、Java8はJavaプラットフォームの画期的なリリースです。特にラムダの導入により、言語が大幅に変更されました。ラムダは、Javaに関数型プログラミング機能を導入する動きを表しています。 Stream APIは、ラムダが、すでに慣れている標準のJavaツールでの作業方法をどのように変えることができるかを示す良い例です。
また、Java 8には、操作するためのいくつかの新機能があります。 非同期プログラミング そして、その日付と時刻のツールの待望のオーバーホール。
一緒に、これらの変更はJava言語の大きな前進を表しており、 Java開発 より面白く、より効率的です。