To begin with, let's have a simple scenario to highlight this problem. This scenario may materialize in the following simple class:
public class Function {
private final int x;
public Function(int x) {
this.x = x;
}
public int xMinusY(int y) {
return x - y;
}
public static int oneMinusY(int y) {
return 1 - y;
}
}
Notice that the preceding snippet of code doesn't assume any range restrictions over x and y. Now, let's impose the following ranges (this is very common with mathematical functions):
- x must be between 0 (inclusive) and 11 (exclusive), so x belongs to [0, 11].
- In the xMinusY() method, y must be between 0 (inclusive) and x (exclusive), so y belongs to [0, x].
- In the oneMinusY() method, y must be between 0 (inclusive) and 16 (exclusive), so y belongs to [0, 16).
These ranges can be imposed in code via the if statements, as follows:
public class Function {
private static final int X_UPPER_BOUND = 11;
private static final int Y_UPPER_BOUND = 16;
private final int x;
public Function(int x) {
if (x < 0 || x >= X_UPPER_BOUND) {
throw new IndexOutOfBoundsException("...");
}
this.x = x;
}
public int xMinusY(int y) {
if (y < 0 || y >= x) {
throw new IndexOutOfBoundsException("...");
}
return x - y;
}
public static int oneMinusY(int y) {
if (y < 0 || y >= Y_UPPER_BOUND) {
throw new IndexOutOfBoundsException("...");
}
return 1 - y;
}
}
Consider replacing IndexOutOfBoundsException with a more meaningful exception (for example, extend IndexOutOfBoundsException and create a custom exception of type, RangeOutOfBoundsException).
Starting with JDK 9, the code can be rewritten to use the Objects.checkIndex() method. This method verifies whether the given index is in the range [0, length] and returns the given index in this range or throws IndexOutOfBoundsException:
public class Function {
private static final int X_UPPER_BOUND = 11;
private static final int Y_UPPER_BOUND = 16;
private final int x;
public Function(int x) {
this.x = Objects.checkIndex(x, X_UPPER_BOUND);
}
public int xMinusY(int y) {
Objects.checkIndex(y, x);
return x - y;
}
public static int oneMinusY(int y) {
Objects.checkIndex(y, Y_UPPER_BOUND);
return 1 - y;
}
}
For example, calling oneMinusY(), as shown in the next code snippet, will result in IndexOutOfBoundsException since y can take values between [0, 16):
int result = Function.oneMinusY(20);
Now, let's go further and check the subrange in a range from 0 to the given length.