Javaの`-able`と`-tor`の違いを図解しながら考えた
JavaのインターフェースであるIterable-Iterator/Comparable-Compatorの存在を初めて知った時、私はかなり混乱してしまいました。その原因は-able
/tor
というサフィックスに惑わされたからです。命名規則は似ているのに、設計思想は似ていなかった。
私はIterable/Iteratorの関係から、XXX-able
に対応するメソッドを提供する目的でXXX-tor
があると勘違いをしていました。ですが、Comparableの場合、実際のメソッドはcompareTo()
が担っています。すると「Comparatorって何なの?」となってしまったのです。
この混乱を解消するために自分なりに整理した内容を公開します。
-able
はインターフェース、では-tor
は?
JavaにおけるIterable
/Comparable
はともにインターフェースです。
インターフェースを継承したオブジェクトは、必ず指定されたメソッドを有しています。
Iterable
の場合、指定メソッドはiterator()
です。このメソッドはIteratorインターフェースを実装したオブジェクトを返します。そう、Iterator
もインターフェースだったのです。インターフェースなので、指定メソッドhasNext()
/next()
を持っています。
Comparable
もインターフェースですので、実装時には指定メソッドのcompareTo()
を必ず持つようになります。そしてComparator
もインターフェースであり、同様に指定メソッドであるcompare()
を持ちます。どちらのインターフェースも比較基準のロジックを提供しますが、Comparable
は自然な順序付けになるデフォルトの基準を提供しています。独自の比較基準はComparator
が対応します。
特性 | Comparable | Comparator |
---|---|---|
用途 | クラス自体に自然な順序を定義する | クラスを変更せず異なる順序を定義する |
実装場所 | クラス自体が実装する | 別クラスとして実装 |
主なメソッド | compareTo() | compare() |
importの必要性 | 必要なし(Comparable は明示的import不要) | 必要(Comparator をimportする必要あり) |
イラストで理解する
IterationとComparisonでインターフェースの設計思想が違ために混乱を引き起こしていたので、イラストを使ってイメージに起こしてみました。
Iterationの場合
Iterator
とは空処理判定とNextボタンを備えた反復処理装置です。最低1つは標準となる反復処理を定めて置かなければなりません。必要であるならば、名前を変えてロジックが異なるバージョンを複数持つことができます。
そして、Iterable
が、標準の反復処理装置を管理する管理人です。for-loopで呼び出された際などには、デフォルト基準の反復処理装置を暗黙の前提で使用できるのもIterable
のおかげです。また別の反復処理基準で作成されたカスタムIterator
にはIterable
な管理人がいないので、hasNext()
とnext()
を明示的に呼び出さないとループ処理ができないという具合です。
Comparisonの場合
デフォルトのソート基準が記載されたタグを取り付ける作業がComparable
です。デフォルト以外のソート基準タグを取り付ける作業がComparator
という関係です。このオブジェクトに取り付けられたソート基準のタグを見ながらcompareTo()
やcompare()
を実行して中身の優劣判断を行います。優劣判断はソートなど別のメソッドに活用されます。
-able
/-tor
の違いを理解する上でIterable
/Comparable
ともに反復処理、比較基準の標準を提供するインターフェースであると理解することが重要かと思います。
Iteratorを直接実装したらダメなの?
Iterable
をすっ飛ばしてhasNext()
,next()
を直接実装したらどうなのでしょうか。
その場合、インターフェースのメソッド名に縛られず、reset()
のような独自メソッドを追加して特定用途に応じた反復ロジックを構築することも可能です。
しかし、Iterable
を実装しない場合はJavaの標準コレクションと同じAPIで扱えないため、Java標準のツールやメソッドとスムーズに連携できなくなるデメリットが発生します。
for-each
ループやCollection
クラスのユーティリティ関連メソッド(min
,max
,sort
など)が使えなくなります。
そのため、Iterable
を実装することで標準APIとの互換性を保持し、汎用性を高める必要があります。
なお、Iterable
はiterator()
しか実装しません。
カスタムイテレータを実装した場合は、実質的には直接実装と変わらないのでループ処理ではIterator
を手動で呼び出す必要があります。
// 逆送り: 明示的にreverseIterator()を利用
Iterator<String> reverseIterator = collection.reverseIterator();
while (reverseIterator.hasNext()) {
System.out.println(reverseIterator.next());
}
設計意図のまとめ
Iterable
/ Iterator
の場合
- 反復処理を明確に分離
- Iterable は「反復処理が可能であること」を定義するインターフェース。
- Iterator は「反復処理をどう実行するか」を提供するオブジェクト。
- 柔軟性よりも基本機能の提供が目的
- Iterable と Iterator は主に「1つの反復基準」を前提に設計されており、複数の反復基準を持つ用途には対応していない
Comparable
/ Comparator
の場合
- 柔軟な比較基準の提供
- Comparable によるデフォルトの順序付けと、Comparator によるカスタム順序付けを組み合わせることで、必要に応じた柔軟性を提供。
- 独立性を重視
- Comparable はクラスに自然順序を埋め込みますが、Comparator は外部で定義されるため、クラスを変更せずに利用可能。
本記事がどなたかのお役に立てば幸いです。
Discussion