Skip to Content
Course content

110: Pattern Matching for instanceof

Click on the "Edit" button in the top corner of the screen to edit your slide content.

If you've been writing Java for a while, you've probably spent a fair amount of time writing the same three lines of code over and over: check if an object is a certain type, cast it to that type, and then finally use it. It's repetitive, and honestly, it's a bit of a waste of space. Pattern matching for instanceof finally fixes this.

Defining our Notification system

To show you how this actually cleans up a codebase, let's build a simple notification dispatcher. Imagine we have a base Notification class and a few specific types: one for emails and one for SMS. I'll keep the classes lean since we're focusing on the logic, not the boilerplate.

class Notification {}

class EmailNotification extends Notification {
    String emailAddress = "dev@example.com";
    String subject = "Hello!";
}

class SmsNotification extends Notification {
    String phoneNumber = "555-0123";
    String message = "Your code is 1234";
}

The clunky way we used to do it

Before pattern matching, if I wanted to process these notifications, I had to do a "check-then-cast" dance. I've written this thousands of times, and it always felt like I was stuttering in code. Look at this:

public void send(Notification n) {
    if (n instanceof EmailNotification) {
        EmailNotification email = (EmailNotification) n; // This line is redundant
        System.out.println("Sending email to " + email.emailAddress);
    } else if (n instanceof SmsNotification) {
        SmsNotification sms = (SmsNotification) n; // And again...
        System.out.println("Sending SMS to " + sms.phoneNumber);
    }
}

See that? I'm telling Java "is this an EmailNotification?" and then immediately saying "Okay, I know it is, now treat it like one." It's tedious.

Using pattern matching to cut the noise

Now, here is the modern way. We can declare the variable right inside the instanceof check. If the check returns true, Java automatically casts the object and assigns it to that variable for us.

public void send(Notification n) {
    if (n instanceof EmailNotification email) {
        // 'email' is already cast and ready to go
        System.out.println("Sending email to " + email.emailAddress);
    } else if (n instanceof SmsNotification sms) {
        System.out.println("Sending SMS to " + sms.phoneNumber);
    }
}

It's much tighter. I'm just saying, "If this is an EmailNotification, call it email and let me use it."

A mistake I made with variable scope

When I first started using this, I tried to be a bit too clever. I thought I could declare a variable in the if block and then use it later in the method. I tried something like this:

if (n instanceof EmailNotification email) {
    System.out.println("Found an email!");
}
// I tried to use 'email' here, but the compiler yelled at me.
System.out.println(email.subject); 

I forgot that the binding variable (email) only exists within the scope where the instanceof check is true. If the check fails, the variable is never created. You can't just leak that variable into the rest of the method. Once you exit that if block, email is gone. It's a small detail, but it tripped me up for a few minutes the first time I used it.

One last tip: you can even use these variables inside more complex boolean expressions. For example, if (n instanceof EmailNotification email && email.subject.contains("Urgent")) works perfectly because Java evaluates from left to right. If it's not an EmailNotification, it stops right there and never tries to access the subject, avoiding a NullPointerException or a class cast error.




📋 Practical Task

Refactoring the Payment Processor

You are working on a legacy payment system. The current processPayment method uses the old "check-then-cast" style. Your task is to refactor this method to use Pattern Matching for instanceof to make the code more concise.

Starting Code:

class Payment {}
class CreditCardPayment extends Payment { String cardNumber = "1234-5678"; }
class PayPalPayment extends Payment { String email = "user@example.com"; }

public class PaymentProcessor {
    public void process(Payment p) {
        if (p instanceof CreditCardPayment) {
            CreditCardPayment cc = (CreditCardPayment) p;
            System.out.println("Processing card: " + cc.cardNumber);
        } else if (p instanceof PayPalPayment) {
            PayPalPayment pp = (PayPalPayment) p;
            System.out.println("Processing PayPal: " + pp.email);
        }
    }
}

Requirements:

  • Remove the explicit casting lines.
  • Incorporate the variable declarations directly into the instanceof checks.
  • Ensure the logic remains exactly the same.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.