Instanceof Pattern Matching, added in JDK 15 (JEP 394), was added to help address the common idiom when using the instanceof
operator of test, assign, convert:
Object actuallyAString = "I'm actually a string!";
if(actuallyAString instanceof String) {//Test if actuallyAString is a String
String nowImAString = //Assign actuallyAString to a variable
(String) actuallyAString; //Convert actuallyAString to a String
System.out.println(nowImAString);
}
The above code, along with being verbose, creates several opportunities for introduction of bugs in the code.
Instanceof pattern matching address this issue by extending the instanceof
operator to take a predicate and a pattern variable that will be assigned when the predicate is true:
Object actuallyAString = "I'm actually a string!";
if(actuallyAString instanceof String nowImAString) {
System.out.println(nowImAString);
}
The pattern variable, in these examples nowImAString
, is only available within the flow scope where it was declared. What this means in practice is that nowImAString
is available everywhere where the compiler definitely knows its value has been assigned (i.e. everywhere where actuallyAString instanceof
is true
)
Typically this will mean the pattern variable will be available in the evaluation block it was declared, and, if part of an if
statement, the following code block, like in the below examples of an if
statement and assigning a boolean
value.
Object actuallyAString = "I'm actually a string!";
if(actuallyAString instanceof String nowImAString) {
System.out.println(nowImAString);
}
System.out.println(nowImAString); //Compiler error, nowImAString not in scope
boolean isAString = (actuallyAString instanceof String nowImAString);
System.out.println(nowImAString); //Compiler error, nowImAString not in scope
However a pattern variable would leave scope to the right of an OR ||
operator as the pattern variable may not had been assigned:
Object actuallyAString = "I'm actually a string!";
if(actuallyAString instanceof String nowImAString
|| nowImAString.endsWith("!") //Compiler error, nowImAString not in scope after ||
) {
System.out.println(nowImAString);
}
The pattern variable would still be in-scope thought to the right of an AND &&
operator:
Object actuallyAString = "I'm actually a string!";
if(actuallyAString instanceof String nowImAString
&& nowImAString.endsWith("!")) {
System.out.println(nowImAString);
}
There are some unusual ways to work with a pattern variable, like in the below examples of encapsulating instanceof
in a NOT !
so that the pattern variable can be referenced in an else
, or throwing an exception in the body of the if
so the pattern variable can be referenced outside of the evaluation and if
code block.
Object actuallyAString = "I'm actually a string!";
if (!(actuallyAString instanceof String nowImAString)) {
//...
} else {
System.out.println(nowImAString);
}
if (!(actuallyAString instanceof String nowImAString)) {
throw new IllegalArgumentException("Must be a string!");
}
System.out.println(nowImAString);
To use value of a pattern variable outside of flow scope, it will need to be assigned to a variable of the scope that is appropriate for your needs, like in the below example of assigning the local variable aString
the value of nowImAString
within the if
block:
Object actuallyAString = "I'm actually a string!";
String aString = null;
if(actuallyAString instanceof String nowImAString) {
aString = nowImAString;
}
System.out.println(aString);