Pattern matching for instanceof (JEP 394)
The motivation in JEP 394 starts out with this statement: “Nearly every program includes some sort of logic that combines testing if an expression has a certain type or structure, and then conditionally extracting components of its state for further processing.” This example follows:
if (obj instanceof String) { String s = (String) obj; // grr... . . . }
One would hope that this kind of type inquiry is not a part of “nearly every program”. But certainly, there are times when it is necessary, and the classic Java approach of instanceof
followed by a cast is rather verbose. Do we really need three occurrences of the type name String
?
That is what “pattern matching for instanceof
” fixes.
As you can see, this form of the instanceof
test declares a variable. If the test passes, the variable is simply set to the value that was tested. The point is that the variable has the correct type. In the example, we can call i.intValue()
and s.length()
since i
is an Integer
and s
is a String
.
As always, if the tested value is null
, the instanceof
test fails.
Variable Scope
When an instanceof
pattern introduces a variable, what is its scope? In the preceding example, it seems clear that the scope of i
is the body of the if
statement. But actually, the scope begins earlier. You can already use the variable in a compound expression that includes the instanceof
test, provided that it is known that the test has passed:
However, the expression
what instanceof Integer i || i.intValue() > 0
would be a compile-time error. The second operand of the ||
operator is executed when the instanceof
test fails. Then i
is not defined.
You can use an ||
if the instanceof
test is negated:
This is perhaps a little mind-bending. If the instanceof
test fails, the negation is true
and the right hand side of the ||
is never executed. In this case, i
is not bound, and the program prints “No good”.
If the instanceof
test passes, the negation is false
, and the right hand side of the ||
is executed. And i
is defined because the test passed.
You can only reach the else
clause when the instanceof
test passes, and therefore, i
is also in scope in the else
clause.
Note that these scope rules do not apply to the &
and |
operators. They don’t short-circuit. That is, both sides are always evaluated, and any variables used in the right operand must be defined unconditionally.
However, the conditional operator is fair game:
Here, i
is in scope in the ?
part.
Useless Matches are Errors
A pattern match that always succeeds is flagged as an error:
Here, the match x instanceof Number n
is useless. It always succeeds and should therefore be omitted. This is a compile-time error.
The test x instanceof Number
is equally useless, but ever since Java 1.0, such tests were allowed.
Use of instanceof
in equals
It has traditionally been rather painful to implement the equals
method:
public final class Point { public int x; public int y; public boolean equals(Object other) { if (other instanceof Point) { Point p = (Point) other; return x == p.x && y == p.y; } }
An instanceof
pattern makes this nicer:
Why are these called patterns?
Java intends to follow other languages that support matching values against patterns with complex criteria. For example, if you have a value what
, you might want to know if it is a point at the origin, another point, or something else entirely:
switch (what) { case Point(0, 0) => ... // It's the point at the origin case Point p => ... // A point other than (0, 0) default => ... // Something else }
Note that in the second case
, a variable p
is introduced so that you can work with the value as a Point
.
Java will, in time, have a syntax more or less like this. For now, the instanceof
with a variable declaration is a “low-hanging fruit” whose syntax resembles what will likely be a part of the pattern matching syntax.