KEMBAR78
Java Pattern Puzzles Java Pattern Puzzles | PPTX
Java Pattern Puzzlers
Simon Ritter, Deputy CTO | Azul
(with thanks to Cay Horstmann)
2
Some Patterns Background
Information
3
Pattern Matching Fundamentals
• A well known technique, been in use since the 1960s (used in Haskell, AWK, etc.)
match predicate pattern variables
Determines whether the pattern
matches a target
A pattern
Conditionally extracted if the
pattern matches the target
5
Patterns In Java
• JEP 394: Pattern matching for instanceof (JDK 16)
• JEP 441: Pattern matching for switch (JDK 21)
• JEP 440: Record patterns (JDK 21)
• JEP 456: Unnamed variables and patterns (JDK 22) [Preview]
• JEP 455: Primitive Types in Patterns, instanceof, and switch (JDK 23) [Preview]
6
Puzzler 1
7
Reusing Variables
• What will happen?
Object s = new Object();
if (s instanceof String s)
System.out.println("String of length " + s.length());
else
System.out.println("No string");
1. The code will work
2. The code will not compile
3. The code will not compile, but it will work
8
The Code Will Not Compile (Using javac)
> javac BadPattern.java
BadPattern.java:5: error: variable s is already defined in method main(String[])
if (s instanceof String s)
^
1 error
Object s = new Object();
if (s instanceof String s)
System.out.println("String of length " + s.length());
else
System.out.println("No string");
9
The Code Will Work (Using jshell)
> jshell
| Welcome to JShell -- Version 23
| For an introduction type: /help intro
jshell> Object s = new Object()
s ==> java.lang.Object@47c62251
jshell> if (s instanceof String s)
...> System.out.println("String of length " + s.length());
...> else
...> System.out.println("No string");
No string
10
Puzzler 2
11
Switch v instanceof <<Abstract>
>
Shape
Triangle
isIsosceles(
)
Square
root()
Pentagon
perimeter(
)
public sealed abstract class Shape permits Triangle, Square, Pentagon { ... }
public sealed class Triangle permits Equilateral, Isosceles extends Shape { ... }
public final class Square extends Shape { ... }
public non-sealed class Pentagon extends Shape { ... }
12
Switch v instanceof
public void shapeTesterA(Shape shape) {
System.out.print("Shape details ");
if (shape instanceof Triangle t) System.out.println(t.isIsosceles());
else if (shape instanceof Square s) System.out.println(s.root());
else if (shape instanceof Pentagon p) System.out.println(p.perimeter());
}
public void shapeTesterB(Shape shape) {
System.out.print("Shape details ");
switch (shape) {
case Triangle t -> System.out.println(t.isIsosceles());
case Square s -> System.out.println(s.root());
case Pentagon p-> System.out.println(p.perimeter());
}
}
Will these deliver the same behaviour?
13
Switch v instanceof
• The two methods will behave in the same way...
• ...with one exception
shapeTesterA(null);
Shape details Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:220)
at com.speakjava.SwitchInstance.shapeTesterB(SwitchInstance.java:8)
at com.speakjava.SwitchInstance.testShape(SwitchInstance.java:29)
at com.speakjava.Main.main(Main.java:28)
Shape details
shapeTesterB(null);
14
Switch v instanceof
• Switch now allows a null to be tested for
• To maintain backward compatibility, a null case is inserted by the compiler if not present
public void shapeTesterB(Shape shape) {
System.out.print("Shape details ");
switch (shape) {
case null -> throw new NullPointerException();
case Triangle t ->
System.out.println(t.isIsosceles());
case Square s -> System.out.println(s.root());
case Pentagon p-> System.out.println(p.perimeter());
}
}
15
Puzzler 3
16
Patterns And Generics
record Box<T>(T contents) { };
Box<String> boxed = null;
String unboxed = switch (boxed) {
case Box(String s) -> s;
};
Exception in thread "main" java.lang.NullPointerException
What will happen?
17
Patterns And Generics
record Box<T>(T contents) { }
Box<Box<String>> doubleBoxed = new Box(null);
String unboxed = switch (doubleBoxed) {
case Box(Box(String s)) -> s;
};
Exception in thread "main" java.lang.MatchException
What will happen now?
18
Puzzler 4
19
Primitives In Switch
public void testObj(Object o) {
switch(o) {
case Integer i -> System.out.println("Integer");
case byte b -> System.out.println("Byte");
default -> System.out.println("Something else");
};
}
What is the result of testObj(Integer.valueOf(42)); ?
Integer
20
Primitives In Switch
public void testObj(Object o) {
switch(o) {
case Integer i -> System.out.println("Integer");
case byte b -> System.out.println("Byte");
default -> System.out.println("Something else");
};
}
What is the result of testObj(42); ?
Integer
21
Primitives In Switch
public void testObj(Object o) {
switch(o) {
case Integer i -> System.out.println("Integer");
case byte b -> System.out.println("Byte");
default -> System.out.println("Something else");
};
}
What is the result of testObj(Byte.valueOf((byte)42); ?
Byte
22
Primitives In Switch
public void testObj(Object o) {
switch(o) {
case Integer i -> System.out.println("Integer");
case byte b -> System.out.println("Byte");
default -> System.out.println("Something else");
};
}
What is the result of testObj((short)256); ?
Something else
23
Puzzler 5
24
Confusingly Guarded Patterns
public sealed abstract class Interrogator permits Who, What, Where, When,
Why { }
public final class When extends
Interrogator {
public boolean when() {
return true;
}
}
25
Confusingly Guarded Patterns
public void testQuestion(Interrogator question) {
switch (question) {
case What what -> System.out.println("What?");
case Where where -> System.out.println("Where?");
case Why why -> System.out.println("Why?");
default -> System.out.println("How?");
}
}
private boolean where() { ... }
private void why() { ... }
private void when() { ... }
26
Confusingly Guarded Patterns
public void testQuestion(Interrogator question) {
switch (question) {
case Who what when where() -> why();
case What what -> System.out.println("What?");
case Where where -> System.out.println("Where?");
case Why why -> System.out.println("Why?");
default -> System.out.println("How?");
}
}
private boolean where() { ... }
private void why() { ... }
private void when() { ... }
27
Confusingly Guarded Patterns
public void testQuestion(Interrogator question) {
switch (question) {
case Who what when where() -> why();
case When when when when.when() -> when();
case What what -> System.out.println("What?");
case Where where -> System.out.println("Where?");
case Why why -> System.out.println("Why?");
default -> System.out.println("How?");
}
}
private boolean where() { ... }
private void why() { ... }
private void when() { ... }
Explain what when means where, and why...
28
Confusingly Guarded Patterns
public void testQuestion(Interrogator question) {
switch (question) {
case Who what when where() -> why();
case When when when when.when() -> when();
case What what -> System.out.println("What?");
case Where where -> System.out.println("Where?");
case Why why -> System.out.println("Why?");
default -> System.out.println("How?");
}
}
private boolean where() { ... }
private void why() { ... }
private void when() { ... }
When is the pattern predicate type
29
Confusingly Guarded Patterns
public void testQuestion(Interrogator question) {
switch (question) {
case Who what when where() -> why();
case When when when when.when() -> when();
case What what -> System.out.println("What?");
case Where where -> System.out.println("Where?");
case Why why -> System.out.println("Why?");
default -> System.out.println("How?");
}
}
private boolean where() { ... }
private void why() { ... }
private void when() { ... }
when is the pattern variable
30
Confusingly Guarded Patterns
public void testQuestion(Interrogator question) {
switch (question) {
case Who what when where() -> why();
case When when when when.when() -> when();
case What what -> System.out.println("What?");
case Where where -> System.out.println("Where?");
case Why why -> System.out.println("Why?");
default -> System.out.println("How?");
}
}
private boolean where() { ... }
private void why() { ... }
private void when() { ... }
when indicates a guarded pattern
31
Confusingly Guarded Patterns
public void testQuestion(Interrogator question) {
switch (question) {
case Who what when where() -> why();
case When when when when.when() -> when();
case What what -> System.out.println("What?");
case Where where -> System.out.println("Where?");
case Why why -> System.out.println("Why?");
default -> System.out.println("How?");
}
}
private boolean where() { ... }
private void why() { ... }
private void when() { ... }
when is the pattern variable used in the guard test
32
Confusingly Guarded Patterns
public void testQuestion(Interrogator question) {
switch (question) {
case Who what when where() -> why();
case When when when when.when() -> when();
case What what -> System.out.println("What?");
case Where where -> System.out.println("Where?");
case Why why -> System.out.println("Why?");
default -> System.out.println("How?");
}
}
private boolean where() { ... }
private void why() { ... }
private void when() { ... }
when is the method of When being called to evaluate the guard
33
Confusingly Guarded Patterns
public void testQuestion(Interrogator question) {
switch (question) {
case Who what when where() -> why();
case When when when when.when() -> when();
case What what -> System.out.println("What?");
case Where where -> System.out.println("Where?");
case Why why -> System.out.println("Why?");
default -> System.out.println("How?");
}
}
private boolean where() { ... }
private void why() { ... }
private void when() { ... }
when is the local method called if the guard test evaluates to true
34
Puzzler 6
35
Scoping In Switch
• Can you declare variables with the same name in different cases?
java: variable d is already defined in method
numberTester(int)
public void numberTester(int n) {
switch (n) {
case 0, 1:
String d = "binary";
log(d);
break;
default:
String d = "not binary";
log(d);
break;
}
}
36
Scoping In Switch
• Local variable scoping
public void numberTester(int n) {
switch (n) {
case 0, 1: {
String d = "binary";
log(d);
break;
}
default: {
String d = "not binary";
log(d);
break;
}
}
}
37
Scoping In Switch
• Patterns use flow scoping
public void shapeTesterC(Shape s) {
String d;
switch (s) {
case Triangle shape -> d = "Triangle " + shape.isIsosceles();
case Square shape -> d = "Square" + shape.root();
case Pentagon shape -> d = "Pentagon " + shape.perimeter();
}
}
Yes, flow scoping is all the places where a variable has definite assignment
Will this code compile?
38
Puzzler 7
39
Sealed Class Shape
Triangle Square Pentagon
public sealed class Shape permits Triangle, Square, Pentagon { ... }
No longer abstract
40
Switch Using Sealed Class
public void shapeTester(Shape s) {
switch (s) {
case Shape shape -> System.out.println("A Shape");
case Triangle triangle -> System.out.println("A Triangle");
case Square square -> System.out.println("A Square");
case Pentagon pentagon -> System.out.println("A Pentagon");
}
}
What is wrong with this switch?
41
Pattern Dominance
public void shapeTester(Shape s) {
switch (s) {
case Shape shape -> System.out.println("A Shape");
case Triangle triangle -> System.out.println("A Triangle");
case Square square -> System.out.println("A Square");
case Pentagon pentagon -> System.out.println("A Pentagon");
}
}
Shape will always match first.
Triangle, Square, Pentagon cases
are unreachable
java: this case label is dominated by a preceding case
label
42
Pattern Dominance
Again, Triangle will match before
(dominate) Triangle with a guard
java: this case label is dominated by a preceding case
label
public void shapeTester(Shape s) {
switch (s) {
case Triangle t -> System.out.println("A Big Triangle");
case Triangle t when t.area() < 25 -> System.out.println("A Small
Triangle");
case Square square -> System.out.println("A Square");
case Pentagon pentagon -> System.out.println("A Pentagon");
case Shape shape -> System.out.println("A Shape");
}
}
43
Pattern Dominance
This is fine. The compiler only cares about type dominance, not guards
public void shapeTester(Shape s) {
switch (s) {
case Triangle t when t.area() < 100 -> System.out.println("A Small
Triangle");
case Triangle t when t.area() < 25 -> System.out.println("A Tiny
Triangle");
case Triangle t -> System.out.println("A Big Triangle");
case Square square -> System.out.println("A Square");
case Pentagon pentagon -> System.out.println("A Pentagon");
case Shape shape -> System.out.println("A Shape");
}
}
44
Pattern Dominance
We can even remove the unguarded Triangle case
public void shapeTester(Shape s) {
switch (s) {
case Triangle t when t.area() < 100 -> System.out.println("A Small
Triangle");
case Triangle t when t.area() < 25 -> System.out.println("A Tiny
Triangle");
case Square square -> System.out.println("A Square");
case Pentagon pentagon -> System.out.println("A Pentagon");
case Shape shape -> System.out.println("A Shape");
}
}
What happens when we call shapeTester(new Triangle(200)); ?
A Shape
45
Puzzler 8
46
Primitive Types In Patterns: Safe Casting
• Exact and unconditionally exact
• The idea is simple:
o reference instanceof primitive_type
o case primitive_type pattern_variable
if (i instanceof byte)
System.out.println("byte");
case byte b -> System.out.println("byte, value is " + b);
47
Primitives With instanceof
int x = 42;
if (x instanceof int)
System.out.println("int"); // prints int (quite logically)
if (x instanceof byte)
System.out.println("byte");
// prints byte (42 fits in a byte)
// prints byte (42 still fits in a byte)
if (x instanceof byte)
System.out.println("byte");
else if (x instanceof int)
System.out.println("int");
if (x instanceof int)
System.out.println("int");
else if (x instanceof byte)
System.out.println("byte");// prints int (first match, not best match)
48
Primitives With switch
int x = 42;
switch (x) {
case byte _ -> System.out.println("byte");
case int _ -> System.out.println("int");
}
What happens?
byte, value is 42
49
Primitives With switch
int x = 42;
switch (x) {
case int _ -> System.out.println("int");
case byte _ -> System.out.println("byte");
}
What happens now?
int dominates byte
java: this case label is dominated by a preceding case label
50
Primitives With switch
• What about if we use a wrapper class?
int x = 42;
switch (x) {
case Integer i -> System.out.println("int, value is "
+ i);
case byte b -> System.out.println("byte, value is " +
b);
}
Will this work?
int, value is 42
51
Primitives With switch
private int x = 128;
switch (x) {
case byte b -> System.out.println("byte, value is " + b);
case short s -> System.out.println("short, value is " + s);
}
Will this work?
java: the switch statement does not cover all possible input values
Completeness still applies with primitives
52
Primitives With switch
private final int x = 128;
switch (x) {
case byte b -> System.out.println("byte, value is " + b);
case short s -> System.out.println("short, value is " + s);
}
Will this work now?
java: the switch statement does not cover all possible input values
The compiler can only do so much
53
Quick Fire Final Round
• True or false?
int x = 65535;
x instanceof byte
x instanceof short
x instanceof int
x instanceof long
x instanceof float
x instanceof double
x instanceof
boolean
false
false // 32767 is biggest short value
true
true
true
true
java: incompatible types: int cannot be converted to boolean
Summary
55
Conclusions
• Pattern matching is a powerful set of language constructs
• Simplifies many tasks
o Less boilerplate code
o More declarative
• Also has the potential for better optimisation of code and enhanced performance
• This is not the end of patterns in Java (see Project Amber)
• Edge cases can require extra thought
• Many thanks to Cay Horstmann for use of material from his blog
https://horstmann.com/unblog/2023-12-01/index.html
Thank you!
@speakjava
57
Extreme Generics
public class StaticSelect<T> {
record A<T>(T value) {}
record Pair<T>(A<T> a, int size) {}
static <T> Pair<T> bar(A<T> a) {
return new Pair<>(a, 0);
}
void foo(A<T> a) {
switch (bar(a)) {
case Pair<T>(/*A<T>*/ var a2, int s) -> {}
}
}
}

Java Pattern Puzzles Java Pattern Puzzles

  • 1.
    Java Pattern Puzzlers SimonRitter, Deputy CTO | Azul (with thanks to Cay Horstmann)
  • 2.
  • 3.
    3 Pattern Matching Fundamentals •A well known technique, been in use since the 1960s (used in Haskell, AWK, etc.) match predicate pattern variables Determines whether the pattern matches a target A pattern Conditionally extracted if the pattern matches the target
  • 4.
    5 Patterns In Java •JEP 394: Pattern matching for instanceof (JDK 16) • JEP 441: Pattern matching for switch (JDK 21) • JEP 440: Record patterns (JDK 21) • JEP 456: Unnamed variables and patterns (JDK 22) [Preview] • JEP 455: Primitive Types in Patterns, instanceof, and switch (JDK 23) [Preview]
  • 5.
  • 6.
    7 Reusing Variables • Whatwill happen? Object s = new Object(); if (s instanceof String s) System.out.println("String of length " + s.length()); else System.out.println("No string"); 1. The code will work 2. The code will not compile 3. The code will not compile, but it will work
  • 7.
    8 The Code WillNot Compile (Using javac) > javac BadPattern.java BadPattern.java:5: error: variable s is already defined in method main(String[]) if (s instanceof String s) ^ 1 error Object s = new Object(); if (s instanceof String s) System.out.println("String of length " + s.length()); else System.out.println("No string");
  • 8.
    9 The Code WillWork (Using jshell) > jshell | Welcome to JShell -- Version 23 | For an introduction type: /help intro jshell> Object s = new Object() s ==> java.lang.Object@47c62251 jshell> if (s instanceof String s) ...> System.out.println("String of length " + s.length()); ...> else ...> System.out.println("No string"); No string
  • 9.
  • 10.
    11 Switch v instanceof<<Abstract> > Shape Triangle isIsosceles( ) Square root() Pentagon perimeter( ) public sealed abstract class Shape permits Triangle, Square, Pentagon { ... } public sealed class Triangle permits Equilateral, Isosceles extends Shape { ... } public final class Square extends Shape { ... } public non-sealed class Pentagon extends Shape { ... }
  • 11.
    12 Switch v instanceof publicvoid shapeTesterA(Shape shape) { System.out.print("Shape details "); if (shape instanceof Triangle t) System.out.println(t.isIsosceles()); else if (shape instanceof Square s) System.out.println(s.root()); else if (shape instanceof Pentagon p) System.out.println(p.perimeter()); } public void shapeTesterB(Shape shape) { System.out.print("Shape details "); switch (shape) { case Triangle t -> System.out.println(t.isIsosceles()); case Square s -> System.out.println(s.root()); case Pentagon p-> System.out.println(p.perimeter()); } } Will these deliver the same behaviour?
  • 12.
    13 Switch v instanceof •The two methods will behave in the same way... • ...with one exception shapeTesterA(null); Shape details Exception in thread "main" java.lang.NullPointerException at java.base/java.util.Objects.requireNonNull(Objects.java:220) at com.speakjava.SwitchInstance.shapeTesterB(SwitchInstance.java:8) at com.speakjava.SwitchInstance.testShape(SwitchInstance.java:29) at com.speakjava.Main.main(Main.java:28) Shape details shapeTesterB(null);
  • 13.
    14 Switch v instanceof •Switch now allows a null to be tested for • To maintain backward compatibility, a null case is inserted by the compiler if not present public void shapeTesterB(Shape shape) { System.out.print("Shape details "); switch (shape) { case null -> throw new NullPointerException(); case Triangle t -> System.out.println(t.isIsosceles()); case Square s -> System.out.println(s.root()); case Pentagon p-> System.out.println(p.perimeter()); } }
  • 14.
  • 15.
    16 Patterns And Generics recordBox<T>(T contents) { }; Box<String> boxed = null; String unboxed = switch (boxed) { case Box(String s) -> s; }; Exception in thread "main" java.lang.NullPointerException What will happen?
  • 16.
    17 Patterns And Generics recordBox<T>(T contents) { } Box<Box<String>> doubleBoxed = new Box(null); String unboxed = switch (doubleBoxed) { case Box(Box(String s)) -> s; }; Exception in thread "main" java.lang.MatchException What will happen now?
  • 17.
  • 18.
    19 Primitives In Switch publicvoid testObj(Object o) { switch(o) { case Integer i -> System.out.println("Integer"); case byte b -> System.out.println("Byte"); default -> System.out.println("Something else"); }; } What is the result of testObj(Integer.valueOf(42)); ? Integer
  • 19.
    20 Primitives In Switch publicvoid testObj(Object o) { switch(o) { case Integer i -> System.out.println("Integer"); case byte b -> System.out.println("Byte"); default -> System.out.println("Something else"); }; } What is the result of testObj(42); ? Integer
  • 20.
    21 Primitives In Switch publicvoid testObj(Object o) { switch(o) { case Integer i -> System.out.println("Integer"); case byte b -> System.out.println("Byte"); default -> System.out.println("Something else"); }; } What is the result of testObj(Byte.valueOf((byte)42); ? Byte
  • 21.
    22 Primitives In Switch publicvoid testObj(Object o) { switch(o) { case Integer i -> System.out.println("Integer"); case byte b -> System.out.println("Byte"); default -> System.out.println("Something else"); }; } What is the result of testObj((short)256); ? Something else
  • 22.
  • 23.
    24 Confusingly Guarded Patterns publicsealed abstract class Interrogator permits Who, What, Where, When, Why { } public final class When extends Interrogator { public boolean when() { return true; } }
  • 24.
    25 Confusingly Guarded Patterns publicvoid testQuestion(Interrogator question) { switch (question) { case What what -> System.out.println("What?"); case Where where -> System.out.println("Where?"); case Why why -> System.out.println("Why?"); default -> System.out.println("How?"); } } private boolean where() { ... } private void why() { ... } private void when() { ... }
  • 25.
    26 Confusingly Guarded Patterns publicvoid testQuestion(Interrogator question) { switch (question) { case Who what when where() -> why(); case What what -> System.out.println("What?"); case Where where -> System.out.println("Where?"); case Why why -> System.out.println("Why?"); default -> System.out.println("How?"); } } private boolean where() { ... } private void why() { ... } private void when() { ... }
  • 26.
    27 Confusingly Guarded Patterns publicvoid testQuestion(Interrogator question) { switch (question) { case Who what when where() -> why(); case When when when when.when() -> when(); case What what -> System.out.println("What?"); case Where where -> System.out.println("Where?"); case Why why -> System.out.println("Why?"); default -> System.out.println("How?"); } } private boolean where() { ... } private void why() { ... } private void when() { ... } Explain what when means where, and why...
  • 27.
    28 Confusingly Guarded Patterns publicvoid testQuestion(Interrogator question) { switch (question) { case Who what when where() -> why(); case When when when when.when() -> when(); case What what -> System.out.println("What?"); case Where where -> System.out.println("Where?"); case Why why -> System.out.println("Why?"); default -> System.out.println("How?"); } } private boolean where() { ... } private void why() { ... } private void when() { ... } When is the pattern predicate type
  • 28.
    29 Confusingly Guarded Patterns publicvoid testQuestion(Interrogator question) { switch (question) { case Who what when where() -> why(); case When when when when.when() -> when(); case What what -> System.out.println("What?"); case Where where -> System.out.println("Where?"); case Why why -> System.out.println("Why?"); default -> System.out.println("How?"); } } private boolean where() { ... } private void why() { ... } private void when() { ... } when is the pattern variable
  • 29.
    30 Confusingly Guarded Patterns publicvoid testQuestion(Interrogator question) { switch (question) { case Who what when where() -> why(); case When when when when.when() -> when(); case What what -> System.out.println("What?"); case Where where -> System.out.println("Where?"); case Why why -> System.out.println("Why?"); default -> System.out.println("How?"); } } private boolean where() { ... } private void why() { ... } private void when() { ... } when indicates a guarded pattern
  • 30.
    31 Confusingly Guarded Patterns publicvoid testQuestion(Interrogator question) { switch (question) { case Who what when where() -> why(); case When when when when.when() -> when(); case What what -> System.out.println("What?"); case Where where -> System.out.println("Where?"); case Why why -> System.out.println("Why?"); default -> System.out.println("How?"); } } private boolean where() { ... } private void why() { ... } private void when() { ... } when is the pattern variable used in the guard test
  • 31.
    32 Confusingly Guarded Patterns publicvoid testQuestion(Interrogator question) { switch (question) { case Who what when where() -> why(); case When when when when.when() -> when(); case What what -> System.out.println("What?"); case Where where -> System.out.println("Where?"); case Why why -> System.out.println("Why?"); default -> System.out.println("How?"); } } private boolean where() { ... } private void why() { ... } private void when() { ... } when is the method of When being called to evaluate the guard
  • 32.
    33 Confusingly Guarded Patterns publicvoid testQuestion(Interrogator question) { switch (question) { case Who what when where() -> why(); case When when when when.when() -> when(); case What what -> System.out.println("What?"); case Where where -> System.out.println("Where?"); case Why why -> System.out.println("Why?"); default -> System.out.println("How?"); } } private boolean where() { ... } private void why() { ... } private void when() { ... } when is the local method called if the guard test evaluates to true
  • 33.
  • 34.
    35 Scoping In Switch •Can you declare variables with the same name in different cases? java: variable d is already defined in method numberTester(int) public void numberTester(int n) { switch (n) { case 0, 1: String d = "binary"; log(d); break; default: String d = "not binary"; log(d); break; } }
  • 35.
    36 Scoping In Switch •Local variable scoping public void numberTester(int n) { switch (n) { case 0, 1: { String d = "binary"; log(d); break; } default: { String d = "not binary"; log(d); break; } } }
  • 36.
    37 Scoping In Switch •Patterns use flow scoping public void shapeTesterC(Shape s) { String d; switch (s) { case Triangle shape -> d = "Triangle " + shape.isIsosceles(); case Square shape -> d = "Square" + shape.root(); case Pentagon shape -> d = "Pentagon " + shape.perimeter(); } } Yes, flow scoping is all the places where a variable has definite assignment Will this code compile?
  • 37.
  • 38.
    39 Sealed Class Shape TriangleSquare Pentagon public sealed class Shape permits Triangle, Square, Pentagon { ... } No longer abstract
  • 39.
    40 Switch Using SealedClass public void shapeTester(Shape s) { switch (s) { case Shape shape -> System.out.println("A Shape"); case Triangle triangle -> System.out.println("A Triangle"); case Square square -> System.out.println("A Square"); case Pentagon pentagon -> System.out.println("A Pentagon"); } } What is wrong with this switch?
  • 40.
    41 Pattern Dominance public voidshapeTester(Shape s) { switch (s) { case Shape shape -> System.out.println("A Shape"); case Triangle triangle -> System.out.println("A Triangle"); case Square square -> System.out.println("A Square"); case Pentagon pentagon -> System.out.println("A Pentagon"); } } Shape will always match first. Triangle, Square, Pentagon cases are unreachable java: this case label is dominated by a preceding case label
  • 41.
    42 Pattern Dominance Again, Trianglewill match before (dominate) Triangle with a guard java: this case label is dominated by a preceding case label public void shapeTester(Shape s) { switch (s) { case Triangle t -> System.out.println("A Big Triangle"); case Triangle t when t.area() < 25 -> System.out.println("A Small Triangle"); case Square square -> System.out.println("A Square"); case Pentagon pentagon -> System.out.println("A Pentagon"); case Shape shape -> System.out.println("A Shape"); } }
  • 42.
    43 Pattern Dominance This isfine. The compiler only cares about type dominance, not guards public void shapeTester(Shape s) { switch (s) { case Triangle t when t.area() < 100 -> System.out.println("A Small Triangle"); case Triangle t when t.area() < 25 -> System.out.println("A Tiny Triangle"); case Triangle t -> System.out.println("A Big Triangle"); case Square square -> System.out.println("A Square"); case Pentagon pentagon -> System.out.println("A Pentagon"); case Shape shape -> System.out.println("A Shape"); } }
  • 43.
    44 Pattern Dominance We caneven remove the unguarded Triangle case public void shapeTester(Shape s) { switch (s) { case Triangle t when t.area() < 100 -> System.out.println("A Small Triangle"); case Triangle t when t.area() < 25 -> System.out.println("A Tiny Triangle"); case Square square -> System.out.println("A Square"); case Pentagon pentagon -> System.out.println("A Pentagon"); case Shape shape -> System.out.println("A Shape"); } } What happens when we call shapeTester(new Triangle(200)); ? A Shape
  • 44.
  • 45.
    46 Primitive Types InPatterns: Safe Casting • Exact and unconditionally exact • The idea is simple: o reference instanceof primitive_type o case primitive_type pattern_variable if (i instanceof byte) System.out.println("byte"); case byte b -> System.out.println("byte, value is " + b);
  • 46.
    47 Primitives With instanceof intx = 42; if (x instanceof int) System.out.println("int"); // prints int (quite logically) if (x instanceof byte) System.out.println("byte"); // prints byte (42 fits in a byte) // prints byte (42 still fits in a byte) if (x instanceof byte) System.out.println("byte"); else if (x instanceof int) System.out.println("int"); if (x instanceof int) System.out.println("int"); else if (x instanceof byte) System.out.println("byte");// prints int (first match, not best match)
  • 47.
    48 Primitives With switch intx = 42; switch (x) { case byte _ -> System.out.println("byte"); case int _ -> System.out.println("int"); } What happens? byte, value is 42
  • 48.
    49 Primitives With switch intx = 42; switch (x) { case int _ -> System.out.println("int"); case byte _ -> System.out.println("byte"); } What happens now? int dominates byte java: this case label is dominated by a preceding case label
  • 49.
    50 Primitives With switch •What about if we use a wrapper class? int x = 42; switch (x) { case Integer i -> System.out.println("int, value is " + i); case byte b -> System.out.println("byte, value is " + b); } Will this work? int, value is 42
  • 50.
    51 Primitives With switch privateint x = 128; switch (x) { case byte b -> System.out.println("byte, value is " + b); case short s -> System.out.println("short, value is " + s); } Will this work? java: the switch statement does not cover all possible input values Completeness still applies with primitives
  • 51.
    52 Primitives With switch privatefinal int x = 128; switch (x) { case byte b -> System.out.println("byte, value is " + b); case short s -> System.out.println("short, value is " + s); } Will this work now? java: the switch statement does not cover all possible input values The compiler can only do so much
  • 52.
    53 Quick Fire FinalRound • True or false? int x = 65535; x instanceof byte x instanceof short x instanceof int x instanceof long x instanceof float x instanceof double x instanceof boolean false false // 32767 is biggest short value true true true true java: incompatible types: int cannot be converted to boolean
  • 53.
  • 54.
    55 Conclusions • Pattern matchingis a powerful set of language constructs • Simplifies many tasks o Less boilerplate code o More declarative • Also has the potential for better optimisation of code and enhanced performance • This is not the end of patterns in Java (see Project Amber) • Edge cases can require extra thought • Many thanks to Cay Horstmann for use of material from his blog https://horstmann.com/unblog/2023-12-01/index.html
  • 55.
  • 56.
    57 Extreme Generics public classStaticSelect<T> { record A<T>(T value) {} record Pair<T>(A<T> a, int size) {} static <T> Pair<T> bar(A<T> a) { return new Pair<>(a, 0); } void foo(A<T> a) { switch (bar(a)) { case Pair<T>(/*A<T>*/ var a2, int s) -> {} } } }