Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8265981

Compiler implementation for Pattern Matching for switch (Preview)

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P4 P4
    • 17
    • tools
    • None
    • minimal
    • Java API, Language construct
    • SE

      Summary

      Enhance the Java language with pattern matching for switch statements and switch expressions. This allows patterns to appear in case labels, and uses pattern matching to determine which case label matches the selector expression. A new kind of case label, case null, is introduced, such that a switch with case null does not automatically throw NullPointerException when the selector expression is null. The meaning of existing switch statements and switch expressions, without case null, is unchanged. Pattern matching for switch complements pattern matching for instanceof, as finalized in Java 16.

      Problem

      We often want to compare an variable against multiple alternatives. Java supports multi-way comparisons with switch statements and, since Java 14, switch expressions, but unfortunately switch is very limited. You can only switch on values of a few types — numeric types, enum types, and String — and you can only test for exact equality against constants. We might like to use patterns to test the same variable against a number of possibilities, taking a specific action on each, but since the existing switch does not support that, we end up with a chain of if...else tests such as:

      static String formatter(Object o) {
          String formatted = "unknown";
          if (o instanceof Integer i) {
              formatted = String.format("int %d", i);
          } else if (o instanceof Long l) {
              formatted = String.format("long %d", l);
          } else if (o instanceof Double d) {
              formatted = String.format("double %f", d);
          } else if (o instanceof String s) {
              formatted = String.format("String %s", s);
          }
          return formatted;
      }

      This code benefits from pattern matching for instanceof expressions, but it is far from perfect. First and foremost, this approach allows coding errors to remain hidden because we have used an overly general control construct. The intent is to assign something to formatted in each arm of the if...else chain, but there is nothing that enables the compiler to identify and verify this invariant. If some block — perhaps one that is executed rarely — does not assign to formatted, we have a bug. In addition, the above code is not optimizable; absent compiler heroics it will have O(n) time complexity, even though the underlying problem is often O(1).

      If we extend switch statements and switch expressions to work on any type, and allow case labels with patterns rather than just constants, then we could rewrite the above code more clearly and reliably:

      static String formatterPatternSwitch(Object o) {
          return switch (o) {
              case Integer i -> String.format("int %d", i);
              case Long l    -> String.format("long %d", l);
              case Double d  -> String.format("double %f", d);
              case String s  -> String.format("String %s", s);
              default        -> o.toString();
          };
      }

      The intent of this code is clearer because we are using the right control construct: we are saying, "the parameter o matches at most one of the following conditions, so figure it out and evaluate the corresponding arm." As a bonus, this code is optimizable; we are likely to be able to perform the dispatch in O(1) time.

      Solution

      The Java language is enhanced as follows, in both switch statements and switch expressions:

      • Allow patterns as case labels for switch blocks.

      • Allow null as a case label for switch blocks.

      • Introduce a new semantics for switch that uses pattern matching to select a matching switch label when the switch label uses a pattern.

      • The semantics of switch become "null-sensitive": if the value of the selector expression is null and the switch block contains a null label, then this label is said to match. If the value of the selector expression is null and the switch block does not contain the null label, then NullPointerException is thrown as usual. This is a conservative extension to the semantics of switch: all existing switch statements and switch expressions will continue to execute as they do in Java 16.

      • Allow the selector expression of a switch statement or switch expression to be of any type, not just the traditional set of char, byte, short, int, Character, Byte, Short, Integer, String, or enum type. This change is conservative in that an existing switch statement or switch expression that uses case labels with constants will continue to require that the type of the selector expression be one of char, byte, short, int, Character, Byte, Short, Integer, String, or an enum type.

      • Introduce the notion of dominance for switch labels, to prevent switch blocks containing switch labels that are "shadowed" by other labels appearing earlier in the block. For example:

        switch (x) {
            case Object o -> ...
            case String s -> ...
        }

        This switch block is erroneous because any value that matches String s will always match Object o -- we say the switch label case Object o dominates the switch label case String s. Dominance is determined with regards to the order that switch labels appear in the switch block. This means that the following switch statement is permitted:

        switch (x) {
            case String s -> ... 
            case Object o -> ...
        }

        Here, a string value for x will match the first switch label case String s, and any other non-null value will match the second switch label case Object o.

      • Enhance the existing notion of completeness for switch expressions to deal with pattern labels by employing a new concept of pattern totality. switch expressions continue to be restricted so it is not possible for no switch label to match at runtime.

      • Introduce a new kind of pattern, guarded patterns, as a means of refining a pattern with a boolean expression. For example, a value matches the guarded pattern List l && l.length()>2 if it first matches the type pattern List l, and (only if it matches) it additionally contains at least two elements.

      • Introduce a new kind of pattern, parenthesized patterns, to help address some parsing issues with guarded patterns containing nested && expressions

      The standard libraries will be enhanced with java.lang.runtime.SwitchBootstrap (a preview class) with a bootstrap method (`typeSwitch), that will categorize switch selector values to case numbers.

      javac will be enhanced to parse the enhanced Java language, and its Trees API will be enhanced to cover it.

      Specification

      Update on June 4: the specification for the bootstrap method has been changed as per review feedback. The updated full specdiff is attached as specdiff.01-4.zip, diff from the previously approved version is attached as specdiff.00-01-4.zip. These are also available for convenience here, respectivelly:

      Original text: The current draft of the specification is attached here as jep406-20210519-2.zip.

      The specdiff of the proposed API changes is attached as specdiff-20210519.zip and is also available here for convenience: http://cr.openjdk.java.net/~jlahoda/8262891/specdiff.00/overview-summary.html

        1. jep406-20210420.zip
          25 kB
        2. jep406-20210514.zip
          28 kB
        3. jep406-20210519.zip
          28 kB
        4. jep406-20210519-1.zip
          28 kB
        5. jep406-20210519-2.zip
          28 kB
        6. jep406-20210527.zip
          29 kB
        7. pattern-switch-changes.diff
          39 kB
        8. specdiff.00-01.zip
          3.34 MB
        9. specdiff.00-01-2.zip
          3.34 MB
        10. specdiff.00-01-3.zip
          3.36 MB
        11. specdiff.00-01-4.zip
          3.34 MB
        12. specdiff.01.zip
          5.93 MB
        13. specdiff.01-2.zip
          5.93 MB
        14. specdiff.01-3.zip
          5.93 MB
        15. specdiff.01-4.zip
          5.93 MB
        16. specdiff.preview.00.zip
          3.55 MB
        17. specdiff-20210519.zip
          3.57 MB

            jlahoda Jan Lahoda
            gbierman Gavin Bierman
            Alex Buckley
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: