[Java] コレクションをよりコレクションらしくする - Iterable
概要
// 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();
}
}
LottoNumbers
はLottoNumber
をリストとして保持するファーストクラスコレクションです。リストが空かどうかを確認するために、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
を活用しましょう。