JEP 305: Pattern Matching for instanceof (Preview)

AuthorBrian Goetz
OwnerGavin Bierman
TypeFeature
ScopeSE
StatusCandidate
Componentspecification / language
Discussionamber dash dev at openjdk dot java dot net
Reviewed byMark Reinhold
Created2017/05/30 19:48
Updated2018/11/02 21:13
Issue8181287

Summary

Enhance the Java programming language with pattern matching for the instanceof operator. Pattern matching allows common logic in a program -- conditionally extracting components from objects -- to be expressed more concisely and safely.

Motivation

Nearly every program includes some sort of logic that combines testing if an expression has a certain type or structure, and then conditionally extracting components of its state for further processing. For example, all Java programmers are familiar with the instanceof-and-cast idiom:

if (obj instanceof String) {
    String s = (String) obj;
    // use s
}

There are three things going on here: a test (is obj a String?), a conversion (casting obj to String), and the declaration of a new local variable (s) so we can use the string value. This pattern is straightforward and understood by all Java programmers, but is suboptimal for several reasons. It is tedious; doing both the type test and cast should be unnecessary (what else would you do after an instanceof test?). This boilerplate -- in particular, the three occurrences of the type String --- obfuscates the more significant logic that follows. But most importantly, the repetition provides opportunities for errors to creep unnoticed into programs.

Rather than reach for ad-hoc solutions, we believe it is time for Java to embrace pattern matching. Pattern matching allows the desired 'shape' of an object to be expressed concisely (the pattern), and for various statements and expressions to test that 'shape' against their input (the matching). Many languages, from Haskell to C#, have embraced pattern matching for its brevity and safety.

Description

A pattern is a combination of (1) a predicate that can be applied to a target, and (2) a set of binding variables that are extracted from the target only if the predicate successfully applies to it. The scope of the binding variables is determined by the context in which the pattern appears.

A type test pattern consists of a predicate that specifies a type, along with a single binding variable.

The instanceof operator (JLS 15.20.2) is extended to take a type test pattern instead of just a type. In the code below, the phrase "String s" is the type test pattern:

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

The instanceof operator "matches" the target obj to the type test pattern as follows: if obj is an instance of String, then it is cast to String and assigned to the binding variable s. The variable is in scope only for the true block of the if statement.

There are no changes to how instanceof works when the target is null. That is, the pattern will only match, and s will only be assigned, if obj is not null.

The use of pattern matching in instanceof should dramatically reduce the overall number of explicit casts in Java programs. Moreover, type test patterns are particularly useful when writing equality methods. Consider the following equality method taken from Item 10 of the Effective Java book:

@Override public boolean equals(Object o) { 
    return (o instanceof CaseInsensitiveString) && 
        ((CaseInsensitiveString) o).s.equalsIgnoreCase(s); 
}

Using a type test pattern means it can be rewritten to the clearer:

@Override public boolean equals(Object o) { 
    return (o instanceof CaseInsensitiveString cis) && 
        cis.s.equalsIgnoreCase(s); 
}

The instanceof grammar is extended accordingly:

RelationalExpression:
     ...
     RelationalExpression instanceof ReferenceType
     RelationalExpression instanceof Pattern

Pattern:
     ReferenceType Identifier

Future Work

Future JEPs will enhance the Java programming language with pattern matching for other language constructs, such as switch expressions and statements.

Alternatives

The benefits of type-test patterns could be obtained by flow typing in if statements, or by a type switch construct. Pattern matching generalizes both of these constructs.

Dependencies

The implementation will likely make use of Dynamic Constants in the JVM. Pattern matching will likely be used in combination with switch expressions.