メインコンテンツにスキップ

[Java] コレクションをよりコレクションらしくする - Iterable

· 4分の読み時間
Haril Song
Owner, Software Engineer at 42dot

概要

// Iterableを実装するJavaのコレクション。
public interface Collection<E> extends Iterable<E>

ファーストクラスコレクションはオブジェクトを扱う上で非常に便利な方法です。しかし、「ファーストクラスコレクション」という名前にもかかわらず、実際にはCollectionをフィールドとして保持しているだけで、実際にはCollectionではないため、Collectionが提供するさまざまなメソッドを使用することはできません。この記事では、Iterableを使用してファーストクラスコレクションをより実際のCollectionに近づける方法を紹介します。

簡単な例を見てみましょう。

@Value
public class LottoNumber {
int value;

public static LottoNumber create(int value) {
return new LottoNumber(value);
}
}
public class LottoNumbers {

private final List<LottoNumber> lottoNumbers;

private LottoNumbers(List<LottoNumber> lottoNumbers) {
this.lottoNumbers = lottoNumbers;
}

public static LottoNumbers create(LottoNumber... numbers) {
return new LottoNumbers(List.of(numbers));
}

// Listのメソッドを使用するためにisEmpty()メソッドを委譲します。
public boolean isEmpty() {
return lottoNumbers.isEmpty();
}
}

LottoNumbersLottoNumberをリストとして保持するファーストクラスコレクションです。リストが空かどうかを確認するために、isEmpty()を実装しています。

isEmpty()の簡単なテストを書いてみましょう。

@Test
void isEmpty() {
LottoNumber lottoNumber = LottoNumber.create(7);
LottoNumbers lottoNumbers = LottoNumbers.create(lottoNumber);

assertThat(lottoNumbers.isEmpty()).isFalse();
}

悪くはありませんが、AssertJはコレクションをテストするためのさまざまなメソッドを提供しています。

  • has..
  • contains...
  • isEmpty()

ファーストクラスコレクションはCollectionではないため、これらの便利なアサートメソッドを使用することはできません。

より正確には、iterator()がないと要素を反復処理できないため、これらを使用することができません。iterator()を使用するには、Iterableを実装するだけです。

実装は非常に簡単です。

public class LottoNumbers implements Iterable<LottoNumber> {

//...

@Override
public Iterator<LottoNumber> iterator() {
return lottoNumbers.iterator();
}
}

ファーストクラスコレクションはすでにCollectionを持っているので、isEmpty()を委譲したのと同じように、単にそれを返すだけです。

@Test
void isEmpty_iterable() {
LottoNumber lottoNumber = LottoNumber.create(7);
LottoNumbers lottoNumbers = LottoNumbers.create(lottoNumber);

assertThat(lottoNumbers).containsExactly(lottoNumber);
assertThat(lottoNumbers).isNotEmpty();
assertThat(lottoNumbers).hasSize(1);
}

これでさまざまなテストメソッドを使用できるようになりました。

テストだけでなく、機能の実装においても便利に使用できます。

for (LottoNumber lottoNumber : lottoNumbers) {
System.out.println("lottoNumber: " + lottoNumber);
}

これはforループがiterator()を使用するため可能です。

結論

Iterableを実装することで、より豊富な機能を使用することができます。実装は難しくなく、機能拡張に近いので、ファーストクラスコレクションを持っている場合は積極的にIterableを活用しましょう。