The instanceof Pattern Match Operator
The instanceof operator can also be used for pattern matching, as explained below. It has the following syntax when used as a pattern match operator:
Note that the syntax of the instanceof pattern match operator augments the syntax of the instanceof type comparison operator with a pattern variable. The type pattern comprises a local reference variable declaration. The instanceof pattern match operator essentially combines the instanceof type comparison operator with a type cast and initialization of a pattern variable.
As with the instanceof type comparison operator, a compile-time check determines whether there is a subtype–supertype relationship between the static type of the left-hand operand and the destination type. However, as opposed to the instanceof type comparison operator, the compiler flags an error if the destination type is not a subtype of the static type of the left-hand operand.
At runtime, if the reference value of the object resulting from the evaluation of the reference expression can be cast to the destination type (i.e., the dynamic type of the resulting object is a subtype of the destination type), the object is said to match the type pattern. Only in this case does the instanceof pattern match operator return true and, as a side effect, the reference value of the object is cast to the destination type and assigned to the pattern variable. The pattern variable is never created or initialized if the instanceof pattern match operator returns false. It always returns false if the left-hand operand is null.
We assume the following types in the discussion below. See Example 5.10, p. 241, for type declarations.
interface IStack (defines methods push() and pop())
interface ISafeStack extends IStack (with boolean methods isEmpty() and isFull())
class Stack implements IStack
class SafeStack extends Stack implements ISafeStack
The instanceof-and-cast idiom is commonly used to elicit the subtype-specific behavior of an object referenced by a supertype reference. The code below at (1) and (2) is equivalent. However, this idiom can be expressed compactly with the instanceof pattern match operator as it type checks the object and casts its reference value that is assigned to the pattern variable. When this operator returns true, it is said to introduce a pattern variable into a well-defined scope, which in this case is the if block.
IStack stack = new SafeStack(20); // Supertype reference denotes subtype object.
// Using the instanceof type comparison operator. (1)
if (stack instanceof SafeStack) { // Correct subtype?
SafeStack safestack = (SafeStack) stack; // Cast to subtype.
System.out.println(safestack.isFull()); // Call subtype-specific method.
}
// Using the instanceof pattern match operator. (2)
if (stack instanceof SafeStack safestack) {
System.out.println(safestack.isFull());
}
In contrast to the instanceof type comparison operator, the destination type in the instanceof pattern match operator must be a subtype of the static type of the left-hand operand. In other words, the reference value of the object is always cast to a subtype by the operator. In the code below, the compiler complains that the expression type (ISafeStack) cannot be a subtype of the pattern types ISafeStack and IStack, respectively, at (1) and (2), in the context of the instanceof pattern match operator.
ISafeStack safestack = new SafeStack(20);
if (safestack instanceof ISafeStack stack) { // (1) Compile-time error!
stack.push(“Hi”);
}
if (safestack instanceof IStack stack) { // (2) Compile-time error!
stack.push(“Howdy”);
}
A pattern variable cannot shadow another local variable by the same name—that is, it cannot be redeclared in a pattern if the local variable of the same name is still in scope.
IStack stack = new SafeStack(20);
SafeStack safestack = new SafeStack(5); // Local variable safestack
if (stack instanceof SafeStack safestack) { // Error: safestack redeclared.
System.out.println(safestack.isFull());
}
A pattern variable introduced by the instanceof pattern match operator can shadow a field of the same name in the class.
// Field declaration:
private static IStack myStack = new Stack(10);
…
IStack stack = new SafeStack(20);
if (stack instanceof SafeStack myStack) { // Local variable introduced.
System.out.println(myStack.isFull()); // Shadows field reference.
}
myStack.push(“Hello”); // Field reference.
A pattern variable is not final in its scope unless it is declared final—in other words, the pattern variable can be declared with the modifier final, as shown at (1). Changing the value of a final pattern variable is flagged as a compile-time error, as shown at (2).
IStack stack = new SafeStack(
if (stack instanceof final SafeStack safestack) { // (1) final pattern variable.
safestack = new SafeStack(100); // (2) Compile-time error!
System.out.println(safestack.isFull());
}
Leave a Reply