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, reset
とclear, flip, rewind
等のAPIはこのオブジェクトを操作する側でposition
やlimit
が操作できれば十分な気がする。capacity
はHashMap
やArrayList
と同程度に隠されているべきだと思う。
最後のほうに「メソッドチェーンができます。」みたいに書いてあるけど、そうじゃない感がすごい。
呼出しの連鎖
このクラスのメソッドのうち戻り値を返さないものは、自身を呼び出したバッファの情報を返します。これを応用して、メソッド呼出しを連鎖させることができます。たとえば次のような文があるとしましょう。
b.flip();
b.position(23);
b.limit(42);
これらは、よりコンパクトな形式の一行で置き換えられます。
b.flip().position(23).limit(42);
buffer
がどのような状態遷移を行うのかが、まるでイメージできない。構文ではなく、可読性がfluentlyになるように設計すべきではないだろうか。
良くも悪くもJavaらしいと思う。