At first it seemed to be just a small enhancement: the addition of “Pattern Matching for instanceof” (JEP 305) in Java 14. No more unnecessary casting after an instanceof, that ought to save us a few seconds a day! However, upon further investigation you’ll quickly discover that pattern matching is not just an enhancement, but rather a vital puzzle piece in the grander scheme of things.
Why were switch expressions added to Java, for example? To make them support pattern matching in a later release! And why did Java 14 bring us records and will Java 15 contain sealed types? Because they could work really well with pattern matching in a later release! These new concepts are the foundation upon which advanced pattern matching features will be built.
So attend this session to get all caught up! You’ll hear about type patterns, deconstruction patterns, nested patterns and even how pattern matching could improve serialization in the future. Live coding included, of course!
13. Type pattern
Type pattern
Type pattern
Type pattern
Type pattern
Consists of a predicate that speci es
a type, along with a single binding
variable.
https://www.pexels.com/photo/person-holding-white-chalk-625219/
14. Pattern matching
Pattern matching
Pattern matching
Pattern matching
Pattern matching
Allows the conditional extraction of
components from objects to be
expressed more concisely and safely.
https://www.pexels.com/photo/person-holding-white-chalk-625219/
15. Declaring 'in the
Declaring 'in the
middle'
middle'
if (product instanceof Guitar lesPaul) {
// use lesPaul
}
1
2
3
16. Scoping
Scoping
'Regular' local variable ('block scoping')
'Regular' local variable ('block scoping')
The block in which it is declared.
void playTunedGuitar() {
Guitar lesPaul = new Guitar("Les Paul");
if (!lesPaul.isInTune()) {
Guitar fenderStrat = new Guitar("Fender Stratocaster");
fenderStrat.play();
}
}
1
2
3
4
5
6
7
8
17. Scoping
Scoping
'Regular' local variable ('block scoping')
'Regular' local variable ('block scoping')
The block in which it is declared.
void playTunedGuitar() {
Guitar lesPaul = new Guitar("Les Paul");
if (!lesPaul.isInTune()) {
Guitar fenderStrat = new Guitar("Fender Stratocaster");
fenderStrat.play();
// fenderStrat is in scope
}
// fenderStrat is not in scope
}
1
2
3
4
5
6
7
8
9
10
18. Scoping
Scoping
Pattern binding variable (' ow scoping')
Pattern binding variable (' ow scoping')
The set of places where it would de nitely be
assigned.
if (product instanceof Guitar lesPaul) {
// can use lesPaul here
} else {
// can't use lesPaul here
}
1
2
3
4
5
19. Scoping
Scoping
Pattern binding variable (' ow scoping')
Pattern binding variable (' ow scoping')
The set of places where it would de nitely be
assigned.
if (product instanceof Guitar lesPaul && lesPaul.isInTune()) {
// can use lesPaul here
} else {
// can't use lesPaul here
}
1
2
3
4
5
20. Scoping
Scoping
Pattern binding variable (' ow scoping')
Pattern binding variable (' ow scoping')
The set of places where it would de nitely be
assigned.
boolean isTunedGuitar(Object product) {
if (!(product instanceof Guitar lesPaul)) {
return false;
}
return lesPaul.isInTune();
}
1
2
3
4
5
6
7
21. Scoping
Scoping
Pattern binding variable (' ow scoping')
Pattern binding variable (' ow scoping')
The set of places where it would de nitely be
assigned.
boolean isTunedGuitar(Object product) {
if (!(product instanceof Guitar lesPaul)) {
return false;
}
// This code is only reachable if 'product' is
// a Guitar, so 'lesPaul' is in scope.
return lesPaul.isInTune();
}
1
2
3
4
5
6
7
8
9
22.
23. Scoping
Scoping
Pattern binding variable (' ow scoping')
Pattern binding variable (' ow scoping')
The set of places where it would de nitely be
assigned.
void test(Effect effect) {
if (effect instanceof Reverb stockEffect)
stockEffect.setRoomSize(25);
else if (effect instanceof Delay stockEffect)
stockEffect.setTimeInMs(200);
}
1
2
3
4
5
6
24.
25. Demo
Demo
Simplify implementation of equals
Loop through a set of Effects and apply 'pattern
matching for instanceof'
https://pxhere.com/en/photo/1458897
26. Bene ts
Bene ts
Nearly 100% of casts will just disappear!
More concise
Eliminates cut/paste errors
27. instanceof
instanceof grammar
grammar
The
The instanceof
instanceof grammar is extended accordingly:
grammar is extended accordingly:
RelationalExpression instanceof Pattern
Pattern:
ReferenceType Identifier
RelationalExpression:
1
...
2
RelationalExpression instanceof ReferenceType
3
4
5
6
7
https://openjdk.java.net/jeps/305
28. It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
type pattern
Guitar lesPaul
https://www.pexels.com/photo/gray-metal-statue-of-man-raising-hand-near-dock-825430/
30. Why so serious?
Why so serious?
Surely a less invasive approach exists?
Flow typing has been considered.
It infers re ned types based on past conditionals.
But... it is suited for instanceof checks only.
And pattern matching can be useful for more
language concepts!
https://pxhere.com/en/photo/835435
39. Switch expression
Switch expression
case Delay de -> String.format("Delay active of %d ms.",
case Reverb re -> String.format("Reverb active of type %s
String apply(Effect effect) {
1
return switch(effect) {
2
3
4
default -> String.format("Unknown effect active: %s
5
};
6
}
7
40. Switch expression
Switch expression
case Overdrive ov -> String.format("Overdrive active with ga
case Tremolo tr -> String.format("Tremolo active with depth
case Tuner tu -> String.format("Tuner active with pitch
String apply(Effect effect) {
1
return switch(effect) {
2
case Delay de -> String.format("Delay active of %d ms.",
3
case Reverb re -> String.format("Reverb active of type %s
4
5
6
7
default -> String.format("Unknown effect active: %
8
};
9
}
10
41. Switch expression
Switch expression
case EffectLoop el -> el.getEffects().stream().map(this::apply
String apply(Effect effect) {
1
return switch(effect) {
2
case Delay de -> String.format("Delay active of %d ms.",
3
case Reverb re -> String.format("Reverb active of type %s
4
case Overdrive ov -> String.format("Overdrive active with ga
5
case Tremolo tr -> String.format("Tremolo active with depth
6
case Tuner tu -> String.format("Tuner active with pitch
7
8
default -> String.format("Unknown effect active: %
9
};
10
}
11
42. Switch expression
Switch expression
String apply(Effect effect) {
return switch(effect) {
case Delay de -> String.format("Delay active of %d ms.",
case Reverb re -> String.format("Reverb active of type %s
case Overdrive ov -> String.format("Overdrive active with ga
case Tremolo tr -> String.format("Tremolo active with depth
case Tuner tu -> String.format("Tuner active with pitch
case EffectLoop el -> el.getEffects().stream().map(this::apply
default -> String.format("Unknown effect active: %
};
}
1
2
3
4
5
6
7
8
9
10
11
43. Bene ts
Bene ts
A single expression instead of many assignments
Less error-prone (in adding cases)
More concise
Safer - the compiler can check for missing cases
47. It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
constant pattern
GuitarType.TELECASTER
https://www.pexels.com/photo/gray-metal-statue-of-man-raising-hand-near-dock-825430/
49. Why so serious?
Why so serious?
Surely a less invasive approach exists?
Type switching has been considered.
It enables case labels to specify types, as well as
constants.
But... it is suited for switch statements only.
And pattern matching can be useful for more
language concepts!
https://pxhere.com/en/photo/835435
51. Here be dragons!
Here be dragons!
Here be dragons!
Here be dragons!
Here be dragons!
We can't be sure at all that the
following features will appear in Java
as depicted. They can change a lot in
the meantime.
https://www.pexels.com/photo/dragon-festival-during-nighttime-6068535/
52. Deconstruction patterns
Deconstruction patterns
String apply(Effect effect) {
return switch(effect) {
case Delay de -> String.format("Delay active of %d ms.",
case Reverb re -> String.format("Reverb active of type %s
case Overdrive ov -> String.format("Overdrive active with ga
case Tremolo tr -> String.format("Tremolo active with depth
case Tuner tu -> String.format("Tuner active with pitch
case EffectLoop el -> el.getEffects().stream().map(this::apply
default -> String.format("Unknown effect active: %
};
}
1
2
3
4
5
6
7
8
9
10
11
53. Deconstruction patterns
Deconstruction patterns
case Overdrive(int gain) -> String.format("Overdrive active w
String apply(Effect effect) {
1
return switch(effect) {
2
case Delay de -> String.format("Delay active of %d ms.",
3
case Reverb re -> String.format("Reverb active of type %s
4
5
case Tremolo tr -> String.format("Tremolo active with depth
6
case Tuner tu -> String.format("Tuner active with pitch
7
case EffectLoop el -> el.getEffects().stream().map(this::apply
8
default -> String.format("Unknown effect active: %
9
};
10
}
11
54. Pattern de nition
Pattern de nition
public class Overdrive implements Effect {
private final int gain;
public Overdrive(int gain) {
this.gain = gain;
}
}
1
2
3
4
5
6
7
55. Pattern de nition
Pattern de nition
public pattern Overdrive(int gain) {
gain = this.gain;
}
public class Overdrive implements Effect {
1
private final int gain;
2
3
public Overdrive(int gain) {
4
this.gain = gain;
5
}
6
7
8
9
10
}
11
56. Deconstruction patterns
Deconstruction patterns
case Overdrive(int gain) -> String.format("Overdrive active w
String apply(Effect effect) {
1
return switch(effect) {
2
case Delay de -> String.format("Delay active of %d ms.",
3
case Reverb re -> String.format("Reverb active of type %s
4
5
case Tremolo tr -> String.format("Tremolo active with depth
6
case Tuner tu -> String.format("Tuner active with pitch
7
case EffectLoop el -> el.getEffects().stream().map(this::apply
8
default -> String.format("Unknown effect active: %
9
};
10
}
11
57. Deconstruction patterns
Deconstruction patterns
String apply(Effect effect) {
return switch(effect) {
case Delay(int timeInMs) -> String.format("Delay active of %d
case Reverb(String name, int roomSize) -> String.format("Reve
case Overdrive(int gain) -> String.format("Overdrive active w
case Tremolo(int depth, int rate) -> String.format("Tremolo ac
case Tuner(int pitchInHz) -> String.format("Tuner active with
case EffectLoop(Set<Effect> effects) -> effects.stream().map(t
default -> String.format("Unknown effect active: %s.", effect
};
}
1
2
3
4
5
6
7
8
9
10
11
61. Var and any patterns
Var and any patterns
// Pre-Java 10
Guitar telecaster = new Guitar("Fender Telecaster Baritone Blacktop", G
// Java 10
var telecaster = new Guitar("Fender Telecaster Baritone Blacktop", Gui
1
2
3
4
5
https://openjdk.java.net/jeps/286
62. Var and any patterns
Var and any patterns
static boolean containsReverbAndDelayWithEqualProperties(EffectLoop ef
if (effectLoop instanceof EffectLoop(Delay(int timeInMs), Reverb(St
return timeInMs == roomSize;
}
return false;
}
1
2
3
4
5
6
63. Var and any patterns
Var and any patterns
if (effectLoop instanceof EffectLoop(Delay(var timeInMs), Reverb(v
static boolean containsReverbAndDelayWithEqualProperties(EffectLoop ef
1
2
return timeInMs == roomSize;
3
}
4
return false;
5
}
6
65. Var and any patterns
Var and any patterns
static boolean containsReverbAndDelayWithEqualProperties(EffectLoop ef
if (effectLoop instanceof EffectLoop(Delay(var timeInMs), Reverb(_
return timeInMs == roomSize;
}
return false;
}
1
2
3
4
5
6
66. Optimization
Optimization
String apply(Effect effect) {
return switch(effect) {
case Delay(int timeInMs) -> String.format("Delay active of %d
case Reverb(String name, int roomSize) -> String.format("Reve
case Overdrive(int gain) -> String.format("Overdrive active w
case Tremolo(int depth, int rate) -> String.format("Tremolo ac
case Tuner(int pitchInHz) -> String.format("Tuner active with
case EffectLoop(Set<Effect> effects) -> effects.stream().map(t
default -> String.format("Unknown effect active: %s.", effect
};
}
1
2
3
4
5
6
7
8
9
10
11
69. Bene ts
Bene ts
Better encapsulation
a case branch only receives data that it actually references.
More elegant logic
by using pattern composition
Optimization
through the use of any patterns
70. It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
deconstruction pattern
Delay(int timeInMs)
https://www.pexels.com/photo/gray-metal-statue-of-man-raising-hand-near-dock-825430/
71. It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
var pattern
var timeInMs
https://www.pexels.com/photo/gray-metal-statue-of-man-raising-hand-near-dock-825430/
72. It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
It's a kind of Pattern
any pattern
_
https://www.pexels.com/photo/gray-metal-statue-of-man-raising-hand-near-dock-825430/
74. Pattern Matching Plays
Pattern Matching Plays
Nice With
Nice With
Sealed Types
Sealed Types
Sealed Types
Sealed Types
Sealed Types
and Records
and Records
and Records
and Records
and Records
https://pxhere.com/en/photo/752901
75. Demo
Demo
Convert e ect classes to a record
Acquire constructor, accessor methods etc.
https://pxhere.com/en/photo/1458897
77. Records
Records
Input:
Input:
Commit to the class being a transparent carrier for
its data.
Output:
Output:
constructors
accessor methods
equals()-implementation
hashCode()-implementation
toString()-implementation
deconstruction pattern
78. Demo
Demo
Make Effect a sealed type
Make subclasses final, sealed or non sealed
https://pxhere.com/en/photo/1458897
79. Exhaustiveness
Exhaustiveness
String apply(Effect effect) {
return switch(effect) {
case Delay(int timeInMs) -> String.format("Delay active of %d
case Reverb(String name, int roomSize) -> String.format("Reve
case Overdrive(int gain) -> String.format("Overdrive active w
case Tremolo(int depth, int rate) -> String.format("Tremolo ac
case Tuner(int pitchInHz) -> String.format("Tuner active with
case EffectLoop(Tuner(int pitchInHz), _) -> String.format("The
case EffectLoop(Set<Effect> effects) -> effects.stream().map(t
default -> String.format("Unknown effect active: %s.", effect
};
}
1
2
3
4
5
6
7
8
9
10
11
12
80. Exhaustiveness
Exhaustiveness
String apply(Effect effect) {
return switch(effect) {
case Delay(int timeInMs) -> String.format("Delay active of %d
case Reverb(String name, int roomSize) -> String.format("Reve
case Overdrive(int gain) -> String.format("Overdrive active w
case Tremolo(int depth, int rate) -> String.format("Tremolo ac
case Tuner(int pitchInHz) -> String.format("Tuner active with
case EffectLoop(Tuner(int pitchInHz), _) -> String.format("The
case EffectLoop(Set<Effect> effects) -> effects.stream().map(t
};
}
1
2
3
4
5
6
7
8
9
10
11
https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html
82. Feature Status
Feature Status
Sealed Types
Sealed Types
Java version Feature status JEP
15 Preview
16 Second preview
17 Final ...
JEP 360
JEP 397
83. A Better
A Better
A Better
A Better
A Better
Serialization
Serialization
Serialization
Serialization
Serialization
?
?
?
?
?
84. Here be dragons!
Here be dragons!
Here be dragons!
Here be dragons!
Here be dragons!
We can't be sure at all that the
following features will appear in Java
as depicted. They can change a lot in
the meantime.
https://www.pexels.com/photo/dragon-festival-during-nighttime-6068535/
86. Serialization
Serialization
very important feature
but many people hate its current implementation
Drawbacks
Drawbacks
it undermines the accessibility model
serialization logic is not 'readable code'
it bypasses constructors and data validation
87. Serialization
Serialization
public class EffectLoop implements Effect {
private String name;
private Set<Effect> effects;
public EffectLoop(String name) {
this.name = name;
this.effects = new HashSet<>();
}
}
1
2
3
4
5
6
7
8
9
88. Serialization
Serialization
public pattern EffectLoop(String name, Effect[] effects) {
name = this.name;
effects = this.effects.toArray();
}
public class EffectLoop implements Effect {
1
private String name;
2
private Set<Effect> effects;
3
4
public EffectLoop(String name) {
5
this.name = name;
6
this.effects = new HashSet<>();
7
}
8
9
10
11
12
13
}
14
89. Serialization
Serialization
public static Effectloop deserialize(String name, Effect[] effect
EffectLoop effectLoop = new EffectLoop(name);
for (Effect effect : effects) {
this.effects.add(effect);
}
return effectLoop;
public class EffectLoop implements Effect {
1
private String name;
2
private Set<Effect> effects;
3
4
public EffectLoop(String name) {
5
this.name = name;
6
this.effects = new HashSet<>();
7
}
8
9
10
11
12
13
14
15
}
16
90. Serialization
Serialization
public class EffectLoop implements Effect {
private String name;
private Set<Effect> effects;
public EffectLoop(String name) {
this.name = name;
this.effects = new HashSet<>();
}
@Deserializer
public static Effectloop deserialize(String name, Effect[] effect
EffectLoop effectLoop = new EffectLoop(name);
for (Effect effect : effects) {
this.effects.add(effect);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
91. Some challenges remain
Some challenges remain
Q:
Q: How to support multiple versions of one class?
How to support multiple versions of one class?
A: @Serializer and @Deserializer annotations
could get a property version in the future.
94. Here be super dragons!
Here be super dragons!
Here be super dragons!
Here be super dragons!
Here be super dragons!
We can't be sure that the following
features will appear in Java as
depicted, if at all.
Proceed with caution!
https://www.pexels.com/photo/dragon-festival-during-nighttime-6068535/
95. Pattern bind statements
Pattern bind statements
var reverb = new Reverb("ChamberReverb", 2);
__let Reverb(String name, int roomSize) = reverb;
// do something with name & roomSize
1
2
3
4
5
https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html
96. Pattern bind statements
Pattern bind statements
else throw new IllegalArgumentException("not a Reverb!");
var reverb = new Reverb("ChamberReverb", 2);
1
2
__let Reverb(String name, int roomSize) = reverb;
3
4
5
// do something with name & roomSize
6
97. Guards
Guards
String apply(Effect effect, Guitar guitar) {
1
return switch(effect) {
2
// (...)
3
case Tremolo tr-> String.format("Tremolo active with depth %d
4
case Tuner tu -> String.format("Tuner active with pitch %d. Mut
5
case EffectLoop el -> el.getEffects().stream().map(this::apply
6
default -> String.format("Unknown effect active: %s.", effect)
7
};
8
}
9
https://openjdk.java.net/jeps/8213076
98. Guards
Guards
case Tuner tu && !tu.isInTune(guitar) -> String.format("Guitar
String apply(Effect effect, Guitar guitar) {
1
return switch(effect) {
2
// (...)
3
case Tremolo tr-> String.format("Tremolo active with depth %d
4
5
case EffectLoop el -> el.getEffects().stream().map(this::apply
6
default -> String.format("Unknown effect active: %s.", effect)
7
};
8
}
9
99. Other ideas
Other ideas
Array patterns
Varargs patterns
AND patterns
Patterns in catch clauses
Collection patterns
Record patterns
https://mail.openjdk.java.net/pipermail/amber-spec-experts/2021-January/002758.html
101. Pattern Kinds
Pattern Kinds
Pattern kind Example Purpose
type pattern Guitar lesPaul Perform an instanceof test,
cast the target, and bind it
to a pattern variable.
constant
pattern
GuitarType.TELECASTER Test the target for equality
with a constant.
deconstruction
pattern
Delay(int timeInMs) Perform an instanceof test,
cast the target, destructure
the target and recursively
match the components to
subpatterns.
var pattern var timeInMs Match anything and bind its
target.
any pattern _ Match anything, but bind
nothing.
102. Pattern Contexts
Pattern Contexts
Pattern
context
Example Purpose
instanceof
predicate
product instanceof
Guitar guitar
Test if target matches the
indicated pattern.
switch
statement or
expression
switch (effect) {
case Delay d
}
Test if target matches one
(or more) of the indicated
patterns.
bind
statement
let Reverb(var
name, var roomSize) =
reverb;
Destructure a target using a
pattern.
104. Pattern matching...
Pattern matching...
is a rich feature arc that will play out over several
versions.
allows us to use type patterns in instanceof.
improves switch expressions.
makes destructuring objects as easy (and more
similar to) constructing them.
holds the potential to simplify and streamline much
of the code we write today.