Skip to Content

Coming in Java 16: Pattern Matching for instanceof

A deceptively simple feature covers quite a few interesting use cases

Posted on

In this post, we’ll go over a new feature coming to Java 16 that brings a lot of change, despite how simple it might look at first glance.

Historically when we’ve used the instanceof operator, it’s been up to the developer to perform the inevitable cast when the type check is true. Starting in Java 16, we’ll be able to let Java perform the cast for us! This feature was in preview for Java 14 and Java 15 and will be officially released in Java 16 (meaning we can use it without having to opt-in via a compiler flag).

The Main Use Case - Before Java 16

First, let’s go over how we check the type of an object and then use that object without the new feature.

if(someObject instanceof String) {
    String someString = (String) someObject;
    // Do something with someString...
}

In this specific case, we check to make sure that someObject is an instance of String, and if it is, we manually cast someObject to a String, and put it in a new local variable called someString. That works fine, and if you’ve been using Java for a while, you’ve probably written something like this yourself, or have run into code that does this.

This cast is commonly the very first thing done after the instanceof check, so why not optimize the syntax around that a bit?

Now With Pattern Matching

Now we can define a local variable (someString) that is automatically cast to the type we’re checking against (String) if and only if the instanceof check is true.

if(someObject instanceof String someString) {
    // Do something with someString...
}
// someString is out of scope here

How great is that? We’ve avoided that ugly manual cast when performing a type check!

There are a couple of scoping rules to keep in mind when working with type patterns. First, someString in this case is only in scope for the if block. Once we reach the end of that block, we can’t refer to someString any more.

The second thing to keep in mind is that the variable we define is essentially a local variable. This means we can’t shadow another variable of the same name in the local scope. For example:

// Does not work!

public void doSomething(Object someObject) {
    String someString = getSomeString();
    if(someObject instanceof String someString) {  // COMPILER ERROR
    	// ...
    }
}

In that case we can’t shadow our local someString with another someString defined in our pattern match. We can, however, shadow a field in our class:

// Works
public class SomeClass {
    private String someString = calculateSomeString();

    public void doSomething(Object someObject) {
        if(someObject instanceof String someString) {  // Shadows SomeClass.someString
    	    // "someString" refers to our instanceof instance here
        }
        // "someString" refers to SomeClass.someString again here
    }
}

These scoping rules initially struck me as a bit counterintuitive but since I try not to shadow variables, I figure I’ll get used to it.

That Seems Simple. Is That It?

No! We can do all sorts of things with patern matching for instanceof.

For example, since someString is now defined, we can write conditional tests against it without having to define another nested if statement. In this case, checking that our String starts with “Awesome”:

if(someObject instanceof String someString && someString.startsWith("Awesome")) {
    // Do something with Awesome someString...
}

If all we wanted to do was to test that someObject is a String that starts with “Awesome”, we could write that like this, again taking advantage of pattern matching.

return (someObject instanceof String someString) && someString.startsWith("Awesome");

This is a much simpler version of what we would have had to do before, where we would have had an ugly manual cast:

// Before pattern matching for instanceof 
return (someObject instanceof String) && ((String)someObject).startsWith("Awesome");

Think of how much nicer our equals methods are going to look! Let’s rewrite the equals method on java.lang.Integer:

// Before
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

// After
public boolean equals(Object obj) {
    return (obj instanceof Integer i) && value == i.intValue();
}

What a difference! Now the code is concise and intuitive.

What’s Next?

There is talk about extending pattern matching to switches (both statements and expressions) and to destructuring record classes (a nice feature in Kotlin with data classes). Those all sound pretty exciting, but I’d settle for being able to negate instanceof directly - !instanceof intead of !(t instanceOf S)

Do you have a place in your code where you think pattern matching for instanceof will come in handy? Let me know!