序章
Java1.8とJava9のArrayListのソースを比較していたところ、set(int index, E element)/get(int)メソッドの中身が変わっていることに気づきました。
setメソッド
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
public E set(int index, E element) {
Objects.checkIndex(index, size);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
getメソッド
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
Objectsクラスを見てみる
ObjectsクラスにcheckIndex()なるメソッドが追加されたようです。中身を見てみます。
Checks if the {@code index} is within the bounds of the range from
{@code 0} (inclusive) to {@code length} (exclusive).
<p>{@code index}
<ul>
<li>{@code index < 0}</li>
<li>{@code index >= length}</li>
<li>{@code length < 0}</li>
</ul>
@param index
@param length
@return{@code index}
@throws IndexOutOfBoundsException{@code index}
@since 9
@ForceInline
public static
int checkIndex(int index, int length) {
return Preconditions.checkIndex(index, length, null);
}
Preconditionsなる慣れないクラスが出てきました。
どうやら、Java9で新たに追加されたクラスの様で、パッケージは jdk.internal.util です。
なんとなく、一般開発者はあまり直で触らない方がよさそうな雰囲気のパッケージ名です。深入りせずに、戻ることにします。
Objectsクラスに戻ってみると、checkIndex()メソッド以外にcheckFromToIndex()や、checkFromIndexSize()というメソッドも追加されています。
Checks if the sub-range from {@code fromIndex} (inclusive) to
{@code toIndex} (exclusive) is within the bounds of range from {@code 0}
(inclusive) to {@code length} (exclusive).
<p>
<ul>
<li>{@code fromIndex < 0}</li>
<li>{@code fromIndex > toIndex}</li>
<li>{@code toIndex > length}</li>
<li>{@code length < 0}</li>
</ul>
@param fromIndex
@param toIndex
@param length
@return{@code fromIndex}
@throws IndexOutOfBoundsException
@since 9
public static
int checkFromToIndex(int fromIndex, int toIndex, int length) {
return Preconditions.checkFromToIndex(fromIndex, toIndex, length, null);
}
Checks if the sub-range from {@code fromIndex} (inclusive) to
{@code fromIndex + size} (exclusive) is within the bounds of range from
{@code 0} (inclusive) to {@code length} (exclusive).
<p>
<ul>
<li>{@code fromIndex < 0}</li>
<li>{@code size < 0}</li>
<li>{@code fromIndex + size > length}</li>
<li>{@code length < 0}</li>
</ul>
@param fromIndex
@param size
@param length
@return{@code fromIndex}
@throws IndexOutOfBoundsException
@since 9
public static
int checkFromIndexSize(int fromIndex, int size, int length) {
return Preconditions.checkFromIndexSize(fromIndex, size, length, null);
}
新たなメソッドを使ってみる
とりあえず、使ってみることにします。
import java.util.Objects;
public class CheckSize {
public static void main(String... args) {
checkIndex();
checkFromToIndex();
checkFromIndexSize();
}
private static void checkIndex() {
int a = Objects.checkIndex(2, 8);
System.out.println("checkIndex:" + a);
}
private static void checkFromToIndex() {
int a = Objects.checkFromToIndex(2, 8, 10);
System.out.println("checkFromToIndex:" + a);
}
private static void checkFromIndexSize() {
int a = Objects.checkFromIndexSize(2, 8, 10);
System.out.println("checkFromIndexSize:" + a);
}
}
実行結果はこうなりました。
checkIndex:2
checkFromToIndex:2
checkFromIndexSize:2
JavaDocにも書かれているように、引数に問題が無ければ一番目の引数をそのまま返すようです。
では、不正な値を渡したらどうなるのでしょう?
private static void checkIndex() {
int a = Objects.checkIndex(8, 2);
System.out.println("checkIndex:" + a);
}
こんなエラーを吐きました。
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 8 out-of-bounds for length 2
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:372)
at test.CheckSize.checkIndex(CheckSize.java:16)
at test.CheckSize.main(CheckSize.java:9)
他のメソッドだと以下の通りです。
- checkFromToIndex()メソッドとそのエラー内容
private static void checkFromToIndex() {
int a = Objects.checkFromToIndex(8, 1, 10);
System.out.println("checkFromToIndex:" + a);
}
Exception in thread "main" java.lang.IndexOutOfBoundsException: Range [8, 1) out-of-bounds for length 10
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:76)
at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:295)
at java.base/java.util.Objects.checkFromToIndex(Objects.java:398)
at test.CheckSize.checkFromToIndex(CheckSize.java:22)
at test.CheckSize.main(CheckSize.java:10)
- checkFromIndexSize()メソッドとそのエラー内容
private static void checkFromIndexSize() {
int a = Objects.checkFromIndexSize(2, 1, 1);
System.out.println("checkFromIndexSize:" + a);
}
Exception in thread "main" java.lang.IndexOutOfBoundsException: Range [2, 2 + 1) out-of-bounds for length 1
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromIndexSize(Preconditions.java:82)
at java.base/jdk.internal.util.Preconditions.checkFromIndexSize(Preconditions.java:343)
at java.base/java.util.Objects.checkFromIndexSize(Objects.java:424)
at test.CheckSize.checkFromIndexSize(CheckSize.java:29)
at test.CheckSize.main(CheckSize.java:11)
まとめ
通常のロジックで使うにはちょっとエラーメッセージが不親切、という印象です。自分で丁寧に判定ロジックを書いて、状況に合わせてわかりやすいメッセージを作った方がいいかもしれません。
そもそも、使い道がほとんどなさそうです。フレームワーク側で使うかも?くらいでしょうか。何かこんなパターンの時使えるのでは、という閃きがありましたら、教えてください。
10/5追記
Objects.checkIndexメソッドはArrayList.java以外にどこから呼ばれてるのかなとソースコードをgrep検索したところ、java.util.ImmutableCollections.javaとjava.util.AbstractList.java からも呼ばれていました。とはいえ、この二つは開発者は意識しないと思うクラスなので本当に「へー」程度ですね。