🎨

Javaの`-able`と`-tor`の違いを図解しながら考えた

2024/12/02に公開

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でインターフェースの設計思想が違ために混乱を引き起こしていたので、イラストを使ってイメージに起こしてみました。

difference-between-interaction-and-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との互換性を保持し、汎用性を高める必要があります。

なお、Iterableiterator()しか実装しません。
カスタムイテレータを実装した場合は、実質的には直接実装と変わらないのでループ処理では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