A new language feature in Java 16, called Pattern Matching for instanceof, uses a type pattern instead of just a type for the instanceof operator. The benefit of this feature is that you can avoid unnecessary code that usually has to be used for casting the target object to the type and declaring a new variable for the resulting object.
One of the Agile principles is “simplicity.” Recent Java versions have added or revised several new features to make Java more simple. One such feature is the instanceof operator, which is used to test if an object is of a certain data type. As an example, to test if an object obj is of type String, you would use the instanceof as follows:
if (obj instanceof String) {}
In the example, the instanceof matches the target object obj to type String for conditional processing of the subsequent block of code.
Problem
The problem with the instanceof was that the conditional block typically includes unnecessary code for the extraction of components from the target object such as converting a Collection object c to Stack by casting and declaring a new variable s in the following example:
if (c instanceof Stack) {
Stack s=(Stack)c;
}
The example code is one line only but a larger application with lots of code could introduce errors.
Solution
Java 16 introduced support for pattern matching for the instanceof operator. Pattern matching means that instead of testing the target object with a data type it is tested with a type pattern. For the example in consideration, the type pattern would be Stack s.
if (c instanceof Stack s) {
//pattern variable s gets extracted to be used
} else {
// pattern variable s does not get extracted and cannot be used
}
The type pattern is essentially a predicate that specifies a type, and a pattern variable, which is like a binding variable. For the example type pattern, the predicate, pattern variable, and target object are as follows:
Predicate: Stack
Pattern variable: s
Target object: c
The instanceof determines if the predicate applies to the target object successfully and if it does, the pattern variable is extracted from the object. Essentially, if the target object is of the type specified in the predicate, the target object is cast to the type specified in the predicate and assigned to the pattern variable implicitly.
A Complete Example
Unnecessary code is avoided as the pattern variable in the type pattern is assigned a value only if the type in the predicate gets applied to the target object. First, consider the following example in which the hashcode of a Collection is output by first determining if the Collection is a Stack.
import java.util.Collection;
import java.util.Stack;
public class PatternMatching {
public static void main(String[] argv){
Collection<Stack> c = new Stack();
if (c instanceof Stack) {
Stack s=(Stack)c;
System.out.println("Collection's hashcode is: "+s.hashCode());
} else {
System.out.println("Not a Collection of specified type");
}
}
}
You could use pattern matching to reduce the code as follows:
import java.util.Collection;
import java.util.Stack;
public class PatternMatching {
public static void main(String[] argv){
Collection<Stack> c = new Stack();
if (c instanceof Stack s) {
System.out.println("Collection's hashcode is: "+s.hashCode());
} else {
System.out.println("Not a Collection of specified type");
}
}
}
Compile and run the example application with output:
Collection's hashcode is: 1
Scope of the Pattern Variable
The scope of the pattern variables is only the block of code for which the instanceof evaluates to true, which implies that it is a compile-time error to use a pattern variable if the pattern has not matched and therefore has not been assigned a value. To demonstrate, consider a variation of the same application to test if a Collection c that is actually a Stack is a LinkedList object. What the following example demonstrates is that because c is not a LinkedList object, the pattern variable l in the type pattern LinkedList l is not extracted, and therefore a compile-time error is generated when l is used in the else block.
import java.util.Collection;
import java.util.Stack;
import java.util.LinkedList;
public class PatternMatching {
public static void main(String[] argv){
Collection<Stack> c = new Stack();
if (c instanceof LinkedList l) {
System.out.println("Collection's hashcode is: "+l.hashCode());
} else {
System.out.println("Collection that is not a LinkedList has hashcode : "+l.hashCode());
}
}
}
Compile the application with the following output:
PatternMatching2.java:12: error: cannot find symbol
System.out.println("Collection that is not a LinkedList has hashcode : "+l.hashCode());
^
symbol: variable l
location: class PatternMatching
The error message is generated because the if statement evaluates to false due to which the pattern variable l is not extracted.
Expression Type Cannot be a Subtype of the Pattern Type
The expression type, or the type of the expression or object being tested, must not be a subtype of the pattern type as it would always evaluate to true and therefore is not of much use. The expression type also cannot be the same as the pattern type. For example, in the following application, the literal String “x” is tested to find whether it is an instance of the String type:
public class PatternMatching {
public static void main(String[] argv){
if ("x" instanceof String s) {
}
}
}
When compiled the application generates an error:
PatternMatching.java:5: error: expression type String is a subtype of pattern ty
pe String
if ("x" instanceof String s) {
^
1 error
A pattern variable is like a local variable that is initialized only if the target object is matched with the predicate.
Conclusion
In this article, we discussed a new language feature in Java 16 called Pattern Matching for instanceof, which uses a type pattern instead of just a type for the instanceof operator. The benefit of this feature is that you can avoid unnecessary code that usually has to be used for casting the target object to the type and declaring a new variable for the resulting object. The type pattern includes a pattern variable in addition to a predicate and the pattern variable is assigned a value only if the predicate gets applied to the target object.
Note: Techwell published an article about pattern matching for instanceof when the feature was experimental in Java 14. The feature was finalized in Java 16 and has a different implementation. The examples used in this article are different from the earlier TechWell Insights article’s examples, as the TechWell Insights article’s examples are not accurate in the final implementation of the feature.
User Comments
It was a very good post indeed. I thoroughly enjoyed reading it during my lunchtime. I will surely come and visit this blog more often. Thanks for sharing