Java Operators

Java Control Statements

Object Oriented Programming

Java Built-in Classes

Java File Handling

Java Error & Exceptions

Java Multithreading

Java Synchronization

Java Networking

Java Collections

Java Interfaces

Java Data Structures

Java Collections Algorithms

Advanced Java

Java Miscellaneous

Java APIs & Frameworks

Java Class References

Java Useful Resources

Java - Pattern Matching with instanceof Operator



Pattern matching means the act of checking if a particular data matches with another data in some property or feature, similar to a regular expression. Java 14 introduces the instanceof operator to have a type test pattern as a preview feature.

The type test pattern has a predicate to specify a type with a single binding variable. It is a standard feature of Java from Java 17 onwards.

The Traditional Approach

Previously, in the traditional approach, we had to perform two steps to safely access members of an object. First, we have to verify the object's type at runtime using the instanceof operator. Then, we have to explicitly castthe object to its type if the check was successful.

Syntax

In the syntax below, we're showing a regular approach of testing a person object with the Employee class, and then in an if block, we're typecasting the person to Employee e to perform operations on the Employee object.

if (person instanceof Employee) {
   // Unnecessary casting
   Employee e = (Employee)person;
   return e.getEmployeeId();
}

Drawbacks of the Traditional Approach

The following are the drawbacks of using the traditional approach for the instanceof operator in Java −

  • It repeats the code as it checks the type, then explicitly casts and assigns it to a newly created local variable. This leads to three references to the specific type(eg, "Employee" object as above).
  • If the casting is not handled carefully, it can result in a ClassCastException error at runtime.
  • It reduces the readability if multiple instanceof checks are used, making it complex to understand.

Enhanced instanceof Operator

Java 14 introduced an enhanced for the instanceof operator in the JEP 305, which tests both the parameter object and assigns it to a binding variable of the appropriate type. The main enhancement is the introduction of the type pattern, which consists of the following two things −

  • Predicate − It is a Boolean function with one argument, which checks if the target object is an instance of the specified type.
  • Pattern Variable − Also known as a binding variable, if the predicate is true, the pattern variable is automatically cast to the specified type.

Syntax

In the syntax below, we've used the instanceof operator to test person object is an Employee, as well as assign the person object to the Employee reference e, which is then used to perform operations on the Employee object.

if (person instanceof Employee e) {
   return e.getEmployeeId();
}

Before this enhancement, developers had to typecast the object as shown in the traditional approach.

Scope of Pattern Variables

The scope of a pattern variable is the places where the program can reach only if the instanceof operator is true. The pattern variables store data from the target only if the predicate returns true.

In the syntax below, we're showing in a basicif statement, the pattern variable that is in scope within the if block

if (person instanceof Employee e) {
   
   // "e" is in scope here
   return e.getEmployeeId();

//"e" is not in scope here
return false;

}

In the syntax below, we're showing that the scope of a pattern variable can be extended beyond the statement that introduced it, as we are negating the if instanceof check using the conditional operator(!)

if (!(person instanceof Employee e)) {
   
   // "e" is not in scope here as the predicate "e" instanceof Employee is false
   return false;

//"e" is not in scope here
return e.getEmployeeId();

}

Example of Traditional Approach

In this example, we've defined classes Person, Employee, and Manager. Employee and Manager extend the Person class. In the APITester class, we've defined a method getId() which takes Person as input, and using the instanceof operator, we're testing the type of object as either Employee or Manager, and then based on the result of the if block, we're typecasting the object to either Employee or Manager and return the employeeId or managerId accordingly.

package com.tutorialspoint;

public class APITester {
   public static void main(String[] args) {
      // Create a Manager Instance   
      Person manager = new Manager(23, "Robert");
      // Get and print Id of the manager
      System.out.println(getId(manager));
   }
   // using instanceof operator
   // to test type of Person to be Employee or Manager
   public static int getId(Person person) {
      // If person is Employee, assign it to e
      // in next statement	  
      if (person instanceof Employee) {
         // Unnecessary typecasting	  
         Employee e = (Employee)person;	  
         return e.getEmployeeId();
      } 
      // If person is Manager, assign it to m
      // in same statement	
      else if (person instanceof Manager) {
         // Unnecessary typecasting	  
         Manager m = (Manager)person;
         return m.getManagerId();
      }
      return -1;
   }
}
abstract sealed class Person permits Employee, Manager {
   String name;
   String getName() {
      return name;
   }
}
final class Employee extends Person {
   String name;
   int id;
   Employee(int id, String name){
      this.id = id;
      this.name = name;
   }
   int getEmployeeId() {
      return id;
   }
}
non-sealed class Manager extends Person {
   int id;
   Manager(int id, String name){
      this.id = id;
      this.name = name;
   }
   int getManagerId() {
      return id;
   }
}

Output

Let us compile and run the above program. This will produce the following result −

23

Example Enhanced instanceof Operator

In this example, we've defined classes Person, Employee, and Manager. Employee and Manager extend the Person class. In the APITester class, we've defined a method getId() which takes Person as input, and using the instanceof operator, we're testing the type of object as either Employee or Manager, and then within the same if block, we're assigning the object to either Employee or Manager without typecasting and return the employeeId or managerId accordingly.

package com.tutorialspoint;

public class APITester {
   public static void main(String[] args) {
   
      // Create a Manager Instance   
      Person manager = new Manager(23, "Robert");
	  
      // Get and print Id of the manager
      System.out.println(getId(manager));
   }
   
   // using instanceof operator
   // to test type of Person to be Employee or Manager
   public static int getId(Person person) {
      // If person is Employee, assign it to e
      // in same statement	  
      if (person instanceof Employee e) {
         return e.getEmployeeId();
      } 
      // If person is Manager, assign it to m
      // in same statement	
      else if (person instanceof Manager m) {
         return m.getManagerId();
      }
      return -1;
   }
}
abstract sealed class Person permits Employee, Manager {
   String name;
   String getName() {
      return name;
   }
}
final class Employee extends Person {
   String name;
   int id;
   Employee(int id, String name){
      this.id = id;
      this.name = name;
   }
   int getEmployeeId() {
      return id;
   }
}
non-sealed class Manager extends Person {
   int id;
   Manager(int id, String name){
      this.id = id;
      this.name = name;
   }
   int getManagerId() {
      return id;
   }
}

Output

Let us compile and run the above program. This will produce the following result −

23
Advertisements