Pattern Matching for instanceof [Java 17]

Generally, pattern matching is referred to as testing some data to see if it has a particular structure and verifying it is a match or not, as we do in regular expressions. The following JEPs enhanced this Java feature to be used with instanceof operator to make it more concise and robust.

The pattern matching for instanceof operator avoids the boilerplate code to type test and cast to a variable more concisely. Let us understand pattern matching in-depth with a few examples.

1. Traditional Approach

Many times in an application, we need to check if an object is of type A or B because how we handle each type is different.

For example, a Customer can be of type BusinessCustomer or PersonalCustomer. Based on the type of customer, we may fetch the information based on context. There are three things going on here for each type:

  • a test (customer instanceof PersonalCustomer)
  • a conversion (casting customer to PersonalCustomer or BusinessCustomer)
  • a declaration of a new local variable (pc or bc)

Notice that the casting step seems unnecessary because we have already tested for the instance in the if statement. The pattern-matching enhancement tries to avoid this boilerplate code, as shown in the next section.

Customer customer = null;
String customerName;

if(customer instanceof PersonalCustomer)
{
  PersonalCustomer pc = (PersonalCustomer) customer; //Redundant casting
  customerName = String.join(" ", pc.getFirstName(), pc.getMiddleName(), pc.getLastName());
}
else if(customer instanceof BusinessCustomer)
{
  BusinessCustomer bc = (BusinessCustomer) customer; //Redundant casting
  customerName = bc.getLegalName();
}

2. Pattern Matching

Now, with pattern matching for instanceof, we can write a similar code in the below manner. Here we can reduce the boilerplate code of typecasting (i.e. casting customer to pc).

In the following example, the customer variable is tested with the type PersonalCustomer; if the Predicate is true, it is assigned to the variable pc. Similar pattern checking is done for the BusinessCustomer type.

Customer customer = null;
String customerName;

if(customer instanceof PersonalCustomer pc)
{
  customerName = String.join(" ", pc.getFirstName(), pc.getMiddleName(), pc.getLastName());
}
else if(customer instanceof BusinessCustomer bc)
{
  customerName = bc.getLegalName();
}

So essentially, a type test pattern (used in instanceof) consists of a predicate specifying a type and a single binding variable. we can deduce from the above definition that a pattern is a combination of :

  • a predicate that can be applied to a target, and
  • a set of binding variables that are extracted from the target only if the predicate successfully applies to it

In the code below, the phrase String s is the type test pattern:

if (obj instanceof String s) {
    // can use 's' here
} 

Note that the pattern will only match, and s will only be assigned, if obj is not null.

3. Scope of Variables

we need to be a little careful about the scope of such pattern variables. Note that the assignment of pattern variable happens only when the predicate test is true. This affects the scope of the variable as well.

In the following example, as the print statement will execute only when the predicate fails, so this statement is not even compiled.

if (!(obj instanceof String s)) {
    //System.out.println(s);  //compiler error - cannot access 's' here
}

The variable is available in the enclosing if block, and we cannot access it outside it.

Object obj = new Object();

if (obj instanceof String s) {
  System.out.println(s);    //can use 's' here
} else if (obj instanceof Number n) {
  //System.out.println(s);    //can not use 's' here
}
//System.out.println(s);    //can not use 's' here

If we are writing complex conditional statements in the if-statement then we can use the pattern variable only with && (AND) operator because the variable will be accessed only when the predicate has been tested to be true. We cannot use the variable with || (OR) operator because the predicate may be tested false in a few cases, and then we will be exceptions of different kinds.

//Allowed to use s in complex condition
if (obj instanceof String s && s.startsWith("a")) {
  System.out.println(s);
}

//compiler error
if (obj instanceof String s || s.startsWith("a")) {
  System.out.println(s);  
}

4. Conclusion

In this Java tutorial, we explored the traditional instanceof operator and the enhancements introduced with the pattern matching for instanceof. We learned how the scope of the pattern variable is affected by the result of the test predicate, with a few examples.

Happy Learning !!

Sourcecode on Github

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode