JDK-8235186 : JEP 375: Pattern Matching for instanceof (Second Preview)
  • Type: JEP
  • Component: specification
  • Sub-Component: language
  • Priority: P3
  • Status: Closed
  • Resolution: Delivered
  • Fix Versions: 15
  • Submitted: 2019-12-02
  • Updated: 2021-08-28
  • Resolved: 2020-07-22
Related Reports
Relates :  
Relates :  
Sub Tasks
JDK-8242367 :  
JDK-8244027 :  
JDK-8246156 :  
Description
Summary
----------

Enhance the Java programming language with _pattern matching_ for the `instanceof` operator. [Pattern matching](https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html) allows common logic in a program, namely the conditional extraction of components from objects, to be expressed more concisely and safely. This is a [preview language feature](https://openjdk.java.net/jeps/12) in JDK 15.

History
----------

Pattern matching for `instanceof` was proposed by [JEP 305](https://openjdk.java.net/jeps/305) in mid 2017, and targeted to JDK 14 in late 2019 as a [preview language feature](https://openjdk.java.net/jeps/12). This JEP proposes to re-preview the feature in JDK 15, with no changes relative to the preview in JDK 14, in order to gather additional feedback.

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.

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](https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#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 binding variable is in
scope in the true block of the `if` statement, and not in the false block of the `if` statement.

The scope of a binding variable, unlike the scope of a local variable, is determined by the semantics of the containing expressions and statements. For example, in this code:

    if (!(obj instanceof String s)) {
        .. s.contains(..) ..
    } else {
        .. s.contains(..) ..
    }

the `s` in the true block refers to a field in the enclosing class, and the `s` in the false block refers to the binding variable introduced by the `instanceof` operator.

When the conditional of the `if` statement grows more complicated than a single `instanceof`, the scope of the binding variable grows accordingly. For example, in this code:

    if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}

the binding variable `s` is in scope on the right hand side of the `&&` operator, as well as in the true block. (The right hand side is only evaluated if `instanceof` succeeded and assigned to `s`.) On the other hand, in this code:

    if (obj instanceof String s || s.length() > 5) {.. s.contains(..) ..}

the binding variable `s` is not in scope on the right hand side of the || operator, nor is it in scope in the true block. (`s` at these points refers to a field in the enclosing class.)

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](https://www.oreilly.com/library/view/effective-java-3rd/9780134686097/):


    @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](https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.20) 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.

Comments
I further clarified the phrasing around this preview.
13-05-2020

Done. Thanks.
04-05-2020

[~gbierman] Could you please clarify that phrase, then? Thanks.
01-05-2020

Thanks Mark. It means we are not adding any enhancements for this preview. We have many enhancements planned for the future, although they may come in other JEPs.
30-04-2020

In the History section you write, “No new enhancements to the feature are planned.” Does that mean that no further enhancements are planned in this round of preview, or that no new enhancements are planned for this feature, ever?
30-04-2020

Note: We decided not to include the deconstruction patterns in this second preview release. They've been removed from the JEP and draft spec. This was announced on the amber spec-experts mailing list.
21-04-2020

Thanks Mark! Looks great.
06-03-2020

Looks great to me, thanks!
05-03-2020

I've done a light copy-editing pass to tighten the wording and align some terminology. If this looks okay to you then assign the issue to me and I'll move it to Candidate.
05-03-2020

Extended JEP to cover deconstructor patterns over record types
06-02-2020