jinyux’s diary

IT業界初心者です。勉強したことをまとめています。

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に渡して範囲外のアクセスがないかをチェックしています。
わざわざ新しく引数のチェックのコードを書かなくて済むというわけですね。