Mukai Systems

java.nio.Bufferが難解すぎる

用途はわかる。Parenにも限定的ではあるにせよ、同等の機能がMemoryStreamクラスに用意してある。

問題は、APIが難解すぎるということである。

以下のメソッドは名前から用途が明解である。

Buffer limit(int newLimit)
Buffer position(int newPosition)
boolean hasRemaining()
int capacity()
int limit()
int position()
int remaining()

以下のメソッドが同一クラスに存在するのは、少なくとも私には不明瞭に思える。

Buffer clear()
Buffer reset()

以下のメソッドは名前からbufferに対してどのような処理が行われるのか想像もつかない。

Buffer rewind()
Buffer flip()
Buffer mark()

仕様書を読めば理解することはできる。

インバリアント(不変式)
次のインバリアントは、マーク、位置、リミット、容量の値を表します。
    0 <= mark <= position <= limit <= capacity
新しく作成されたバッファの位置は常にゼロ、マークは未定義です。リミットの初期値はゼロか、バッファの構築方法や種類によってはそれ以外の値になります。新しく割り当てられたバッファの各要素はゼロに初期化されます。

マークとリセット
バッファの「マーク」は、resetメソッドを呼び出したときに戻る位置を指定するインデックスです。定義されていない場合もありますが、定義されている場合は必ず位置以下の正の値になります。位置やリミットの値がマークの値よりも小さい場合、マークは破棄されます。マークが定義されていない場合、resetメソッドを呼び出すと、InvalidMarkExceptionがスローされます。

クリア、フリップ、リワインド
位置、リミット、容量の値にアクセスするメソッドや、マークやリセットを行うメソッドと同様に、このクラスもバッファに対する次のような操作を定義します。
clear()は、新しい一連のチャネル読込み操作または相対「put」操作のためにバッファを準備します。リミットを容量の値に設定し、位置をゼロに設定します。
flip()は、新しい一連のチャネル読込み操作または相対「get」操作のためにバッファを準備します。リミットの値を現在位置の値に合わせたあと、位置の値をゼロにします。
rewind()は、すでにバッファに格納されているデータを再度読み込めるように、バッファを準備します。リミットの値はそのままで、位置の値をゼロにします。

APIを理解したうえで、少なくとも私には誤った抽象化のように思えて仕方がない。mark, resetclear, flip, rewind等のAPIはこのオブジェクトを操作する側でpositionlimitが操作できれば十分な気がする。capacityHashMapArrayListと同程度に隠されているべきだと思う。

最後のほうに「メソッドチェーンができます。」みたいに書いてあるけど、そうじゃない感がすごい。

呼出しの連鎖
このクラスのメソッドのうち戻り値を返さないものは、自身を呼び出したバッファの情報を返します。これを応用して、メソッド呼出しを連鎖させることができます。たとえば次のような文があるとしましょう。
    b.flip();
    b.position(23);
    b.limit(42);
これらは、よりコンパクトな形式の一行で置き換えられます。
    b.flip().position(23).limit(42);

bufferがどのような状態遷移を行うのかが、まるでイメージできない。構文ではなく、可読性がfluentlyになるように設計すべきではないだろうか。

良くも悪くもJavaらしいと思う。