
record Deposit(double amount) {}
record Withdrawal(double amount) {}
record Fee(double amount, String reason) {}
sealed interface BankTransaction permits Deposit, Withdrawal, Fee {
double amount();
}
record Deposit(double amount) implements BankTransaction {}
...
permits clause not required if all subtypes in same source filebalance += switch (bankTransaction) {
case Deposit(var amount) -> amount;
case Withdrawal(var amount) -> -amount;
case Fee(var amount, _) -> -amount;
};

switch again:
balance += switch (bankTransaction) {
case Deposit(var amount) -> amount;
case Withdrawal(var amount) -> -amount;
case Fee(var amount, _) -> -amount;
}
switch is an expression—it yields a value_ doesn't bind a componentinstanceof tests
ShadyCryptoTransactionswitch or instanceofpublic interface BankTransaction {
double value();
}
public class ShadyCryptoTransaction implements BankTransaction {
public double value() { return ... ; } // single point of business logic
}
...
balance += bankTransaction.value(); // polymorphic dispatch

Optionalswitch (myOptional) {
case Of(t) -> do something with t;
case Empty() -> do something about emptiness;
}
BankTransaction interface? Is it sealed or open-ended?
switch branches over “summands” BankTransaction
|
+---------+-----------+
| | |
Deposit Withdrawal Fee

enum AccountCreation implements BankTransaction { INSTANCE }
switch:
switch (transaction) {
case AccountCreation.INSTANCE -> ...
case Deposit(var amount) -> ...
...
}
switch over single enum
switch statement (with patterns) must also be exhaustivecasenull—see next slideswitch no longer compiles MatchException may occurdefault when you really mean “for all other, present or future”case BankTransaction)
switch has always been null-hostilenull selector throws a NullPointerExceptioncase null
default does not cover nullnullnull
record FeeEvent(Instant when, Fee fee) {}
var reason = switch (event) {
case FeeEvent(_, Fee(_, var reason)) -> reason;
};
new Event(Instant.now(), null), a MatchException is thrown
case Withdrawal(var amount) when amount > balance -> throw new BankTransactionException(...);
case Withdrawal(var amount) -> -amount;
case Withdrawal(0) // syntax error case Withdrawal(var amount) when amount == 0 // Ok
null
case FeeEvent(_, var fee) when fee == null

case Fee f -> f.reason();
if (selector instanceof Fee f) ...
instanceof as well:
if (selector instanceof Fee(_, var reason)) ...
switch, instanceof tolerates nullswitch over instanceof—exhaustiveness checkswitch (transaction) {
case Deposit(var amount) -> ...
case Deposit(var amount) when amount < 0 -> ... // Error
case BankTransaction t -> ...
case Withdrawal(var amount) -> ... // Error
...
}
switch over sequence of if with instanceofswitch (transaction) {
case Deposit(var amount) when amount <= 0 -> ...
case Deposit(var amount) when amount == 0 -> ... // Not flagged as error
case Deposit(var amount) -> ...
...
}

sealed interface JSONValue {}
sealed interface JSONPrimitive extends JSONValue {}
record JSONArray(List<JSONValue> elements) implements JSONValue {}
record JSONObject(Map<String, JSONValue> entries) implements JSONValue {}
record JSONString(String value) implements JSONPrimitive {}
record JSONNumber(double value) implements JSONPrimitive {}
enum JSONBoolean implements JSONPrimitive { FALSE, TRUE }
enum JSONNull implements JSONPrimitive { INSTANCE }
enum for finite choicesswitch (json) {
case JSONString(var value) when value.equals("password") -> ...
case JSONObject(var entries) when entries.get("id") instanceof JSONString(var id) -> ...
case JSONBoolean.TRUE -> ...
...
}

boolean id = root.get("transactions").get("0").get("id").asString();
JsonNumber can handle high-precision numberslong id = switch (thread.get("id")) {
case JsonNumber jn -> jn.asLong();
case JsonString js -> Long.parseLong(js.asString());
default -> throw new JsonValueException(...);
};

switch to analyze instancesswitch in C = jump tableswitch with selectors int, short, char, byteString, enum , Integer, Short, Character, Bytematch, Kotlin whenswitch because of familiaritynull, enum, default, primitives vs. wrapper types
enum Color { RED, YELLOW, GREEN };
boolean go(Color c) {
switch (c) {
case RED: return false;
case YELLOW, GREEN: return true;
}
}
enum constant—JLS §14.22Boolean go(Color c) {
switch (c) {
case RED: return false;
case YELLOW, GREEN: return true;
case null: return null;
}
}
case null makes it “enhanced”—§14.11.2
record Amount(Number n) {}
Integer value(Amount p) {
return switch (p) {
case Amount(Integer value) -> value;
case Amount(Number _) -> -1;
case Amount(Object _) -> -2;
};
}
void main() {
IO.println(value(new Amount(null)));
}
NullPointerException or a MatchException?null, -1 or -2?-1.
int n = ...; if (n instanceof byte b) ...
switch (n) {
case byte b -> ...;
...
}
float, double, long, and boolean (and their boxed types):
double x = ...;
switch (x) {
case int n -> ...; // no fractional part, fits in an int
case float f -> ...; // fits losslessly into a float
}

sealed interface Shape {}
record Rect(double x, double y) implements Shape {}
record Square(double x) implements Shape {}
record Circle(double x) implements Shape {}
public List<Shape> dareToBeSquare(List<Shape> shapes) {
return shapes.stream()
.map(s -> switch(s) {
case Rect(int x, _) -> new Square(x);
default -> s;
})
.toList();
}
record Rect(double x, double y) implements Shape {}
case Rect(int x, _) -> new Square(x);
case Rect(var x, _)
switchObject, Numberenum valuesvar in deconstruction