1. 序章

このクイックチュートリアルでは、JavaStreamsを操作するときのStream.filter()メソッドの使用方法について説明します。

それを使用する方法と、チェックされた例外を持つ特殊なケースを処理する方法を見ていきます。

2. Stream.filter()を使用する

filter()メソッドは、 Stream インターフェイスの中間操作であり、特定の述語:に一致するストリームの要素をフィルタリングできます。

Stream<T> filter(Predicate<? super T> predicate)

これがどのように機能するかを確認するために、Customerクラスを作成しましょう。

public class Customer {
    private String name;
    private int points;
    //Constructor and standard getters
}

さらに、顧客のコレクションを作成しましょう。

Customer john = new Customer("John P.", 15);
Customer sarah = new Customer("Sarah M.", 200);
Customer charles = new Customer("Charles B.", 150);
Customer mary = new Customer("Mary T.", 1);

List<Customer> customers = Arrays.asList(john, sarah, charles, mary);

2.1. コレクションのフィルタリング

filter()メソッドの一般的な使用例は、コレクションの処理です。

100 ポイントを超える顧客のリストを作成しましょう。これを行うには、ラムダ式を使用できます。

List<Customer> customersWithMoreThan100Points = customers
  .stream()
  .filter(c -> c.getPoints() > 100)
  .collect(Collectors.toList());

ラムダ式の省略形であるメソッドリファレンスを使用することもできます。

List<Customer> customersWithMoreThan100Points = customers
  .stream()
  .filter(Customer::hasOverHundredPoints)
  .collect(Collectors.toList());

この場合、hasOverHundredPointsメソッドをCustomerクラスに追加しました。

public boolean hasOverHundredPoints() {
    return this.points > 100;
}

どちらの場合も、同じ結果が得られます。

assertThat(customersWithMoreThan100Points).hasSize(2);
assertThat(customersWithMoreThan100Points).contains(sarah, charles);

2.2. 複数の基準を持つコレクションのフィルタリング

さらに、 filter()で複数の条件を使用できます。 たとえば、pointsnameでフィルタリングできます。

List<Customer> charlesWithMoreThan100Points = customers
  .stream()
  .filter(c -> c.getPoints() > 100 && c.getName().startsWith("Charles"))
  .collect(Collectors.toList());

assertThat(charlesWithMoreThan100Points).hasSize(1);
assertThat(charlesWithMoreThan100Points).contains(charles);

3. 例外の処理

これまで、例外をスローしない述語でフィルターを使用してきました。 実際、Javaの機能インターフェイスは、チェックされたまたはチェックされていない例外を宣言しません。

次に、ラムダ式例外を処理するいくつかの異なる方法を示します。

3.1. カスタムラッパーの使用

まず、profilePhotoUrlCustomer に追加することから始めます。

private String profilePhotoUrl;

さらに、単純な hasValidProfilePhoto()メソッドを追加して、プロファイルの可用性を確認しましょう。

public boolean hasValidProfilePhoto() throws IOException {
    URL url = new URL(this.profilePhotoUrl);
    HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
    return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
}

hasValidProfilePhoto()メソッドがIOExceptionをスローすることがわかります。 ここで、この方法で顧客をフィルタリングしようとすると、次のようになります。

List<Customer> customersWithValidProfilePhoto = customers
  .stream()
  .filter(Customer::hasValidProfilePhoto)
  .collect(Collectors.toList());

次のエラーが表示されます。

Incompatible thrown types java.io.IOException in functional expression

これを処理するために使用できる代替手段の1つは、try-catchブロックでラップすることです。

List<Customer> customersWithValidProfilePhoto = customers
  .stream()
  .filter(c -> {
      try {
          return c.hasValidProfilePhoto();
      } catch (IOException e) {
          //handle exception
      }
      return false;
  })
  .collect(Collectors.toList());

述語から例外をスローする必要がある場合は、RuntimeExceptionのようなチェックされていない例外でラップすることができます。

3.2. ThrowingFunctionの使用

または、ThrowingFunctionライブラリを使用することもできます。

ThrowingFunctionは、Java機能インターフェイスでチェックされた例外を処理できるようにするオープンソースライブラリです。

throwing-function依存性をpomに追加することから始めましょう。

<dependency>
    <groupId>pl.touk</groupId>
    <artifactId>throwing-function</artifactId>
    <version>1.3</version>
</dependency>

述語の例外を処理するために、このライブラリは ThrowingPredicate クラスを提供します。このクラスには、チェックされた例外をラップする unchecked()メソッドがあります。

実際の動作を見てみましょう。

List customersWithValidProfilePhoto = customers
  .stream()
  .filter(ThrowingPredicate.unchecked(Customer::hasValidProfilePhoto))
  .collect(Collectors.toList());

4. 結論

この記事では、 filter()メソッドを使用してストリームを処理する方法の例を見ました。 また、例外を処理するためのいくつかの代替案についても検討しました。

いつものように、完全なコードはGitHubから入手できます。