Objects.checkFromIndexSizeについて
Objects.checkFromIndexSize
というメソッドに遭遇したので調べたことを書きます。
まずはソースコードを見てみると、Preconditions.checkFromIndexSize
に委譲されているシンプルなメソッドです。
public static int checkFromIndexSize(int fromIndex, int size, int length) { return Preconditions.checkFromIndexSize(fromIndex, size, length, null); }
このPreconditions
クラスはguavaのPreconditions
クラスとは別者でjdk.internal.util
パッケージに属するクラスです。
このパッケージ自体はJava 9で追加されたもののようで、名前からして普通の開発者が直に触るものではないでしょう。
しかし、見るだけはタダなのでちょっと覗いてみることにします。
public static <X extends RuntimeException> int checkFromIndexSize(int fromIndex, int size, int length, BiFunction<String, List<Number>, X> oobef) { if ((length | fromIndex | size) < 0 || size > length - fromIndex) throw outOfBoundsCheckFromIndexSize(oobef, fromIndex, size, length); return fromIndex; }
fromIndex
, size
, length
の引数を取ります。if文には以下のような条件式が設定されています。
- (length | fromIndex | size)
が負かどうか。
ビット演算で正負の判定をしています。各変数の最上位ビットに一つでも1が入っていた場合、演算結果の最上位ビットに1が入り、負となります。
つまり、3つの変数のどれか一つでも負であった場合、true
となる。
- size > length - fromIndex
例として配列を考えると、fromIndex
からlength
分を取り出そうとするときに、それが配列のsizeを超えているかどうか、のような判定をしたいときに使われる条件で、size
を超えているときにtrue
になる。
つまり、checkFromIndexSize
メソッドは配列の範囲外にアクセスしようとしていないかのチェックに使えるメソッドのようです。
そして、範囲外のアクセスであれば例外を投げます。(Bifunction
型の引数についてはここではおいておきます。)
Objects.checkFromIndexSize
メソッドが使われている例として、BufferedReader.read
メソッドがあります。
public int read(char[] cbuf, int off, int len) throws IOException { synchronized (lock) { ensureOpen(); Objects.checkFromIndexSize(off, len, cbuf.length); if (len == 0) { return 0; } int n = read1(cbuf, off, len); if (n <= 0) return n; while ((n < len) && in.ready()) { int n1 = read1(cbuf, off + n, len - n); if (n1 <= 0) break; n += n1; } return n; } }
read
メソッドはReader
クラスの抽象メソッドで、一般にリソースから引数のlen
だけ読み込んで、cbuf
配列にoff
で指定したindexから書き込むメソッドです。
このときに引数をObjects.checkFromIndexSize
に渡して範囲外のアクセスがないかをチェックしています。
わざわざ新しく引数のチェックのコードを書かなくて済むというわけですね。