## Notes
- Support for changes in MacOS hardware
- [[#Pattern Matching for Switch Statements]]
- [[#Sealed Classes]]
## Pattern Matching for Switch Statements
```java
static record Human (String name, int age, String profession) {}
public String checkObject(Object obj) {
return switch (obj) {
case Human h -> "Name: %s, age: %s, profession: %s".formatted(h.name(), h.age(), h.profession());
case Circle c -> "This is a circle";
case Shape s -> "This is a shape";
case null -> "It is null";
default -> "It is an object";
}
}
public String checkShape(Shape shape) {
return switch (shape) {
case Triangle t && (t.getNumberOfSides() != 3) -> "This is a weird triangle";
case Circle c && (c.getNumberOfSides() != 0) -> "This is a weird circle";
default -> "Just a normal shape";
}
}
```
### References
- [Baeldung - Java 17 Pattern Matching for Switch Statements](https://www.baeldung.com/java-17-new-features#7-pattern-matching-for-switch-preview-jep-406)
## Sealed Classes
- Enables more fine-grained control of inheritance
- Allows classes and interfaces to define their subtypes
### Motivation
#### Package-Private Approach
Users cannot access the *abstract class* without also allowing them to extend it
```java
public class Vehicles {
abstract static class Vehicle {
private final String registrationNumber;
public Vehicle(String registrationNumber) {
this.registrationNumber = registrationNumber;
}
public String getRegistrationNumber() {
return this.registrationNumber;
}
}
public static final class Car extends Vehicle {
private final int numberOfSeats;
public Car(int numberOfSeats, String registrationNumber) {
super(registrationNumber);
this.numberOfSeats = numberOfSeats;
}
public int getNumberOfSeats() {
return this.numberOfSeats;
}
}
public static final class Truck extends Vehicle {
private final int loadCapacity;
public Truck(int loadCapacity, String registrationNumber) {
super(registrationNumber);
this.loadCapacity = loadCapacity;
}
public int getLoadCapacity() {
return this.loadCapacity;
}
}
}
```
#### Superclass Accessible, Not Extensible
A *superclass* developed with a set of *subclasses* should be able to define its usage, not constrain its *subclasses*.
### Creation
#### Sealed Interfaces
Apply `sealed` modifier. `permits` clause specifies the classes allowed to implement the sealed interface.
```java
public sealed interface Service permits Car, Truck {
int getMaxServiceIntervalInMonths;
default int getMaxDistanceBetweenServicesInKilometers() {
return 1000000;
}
}
```
#### Sealed Classes
Like *interfaces*, apply the same `sealed` modifier.
```java
public abstract sealed class Vehicle permits Car, Truck {
protected final String registrationNumber;
public Vehicle(String registrationNumber) {
this.registrationNumber = registrationNumber;
}
public String getRegistrationNumber() {
return registrationNumber;
}
}
```
A permitted *subclass* must define a modifier. It *may* be declared `final` to prevent any further extensions.
```java
public final class Truck extends Vehicle implements Service {
private final int loadCapacity;
public Truck(int loadCapacity, String registrationNumber) {
super(registrationNumber);
this.loadCapacity = loadCapacity;
}
public int getLoadCapacity() {
return this.loadCapacity;
}
@Override
public int getMaxServiceIntervalInMonths() {
return 18;
}
}
```
A permitted *subclass* may also be declared `sealed`. If declared `non-sealed`, subclasses can extend it.
```java
public non-sealed class Car extends Vehicle implements Service {
private final int numberOfSeats;
public Car(int numberOfSeats, String registrationNumber) {
super(registrationNumber);
this.numberOfSeats = numberOfSeats;
}
public int getNumberOfSeats() {
return this.numberOfSeats;
}
@Override
public int getMaxServiceIntervalInMonths() {
return 12;
}
}
```
#### Constraints
Three constraints on its permitted subclasses
1. All permitted subclasses must belong to the same module as the sealed class
2. Every permitted subclass must explicitly extend the sealed class
3. Every permitted subclass must define a modifier: `final`, `sealed`, `non-sealed`
### Usage
#### The Traditional Way
When sealing a class, we enable the client to reason clearly about all permitted subclasses
```java
if (vehicle instanceof Car) {
return ((Car) vehicle).getNumberOfSeats();
} else if (vehicle instanceof Truck) {
return ((Truck) vehicle).getLoadCapacity();
} else {
throw new RuntimeException("Unknown instance of vehicle");
}
```
#### Pattern Matching
Can now use local variables
```java
if (vehicle instanceof Car car) {
return car.getNumberOfSeats();
} else if (vehicle instanceof Truck truck) {
return truck.getLoadCapacity();
} else {
throw new RuntimeException("Unknown instance of Vehicle");
}
```
### Compatibility
Sealed classes can interact with `Record`s and `Reflection`.
#### Records
`Records` are implicitly `final`. Sealed hierarchy is even more concise.
```java
public sealed interface Vehicle permits Car, Truck {
String getRegistrationNumber();
}
public record Car(int numberOfSeats, String registrationNumber) implements Vehicle {
@Override
public String getRegistrationNumber() {
return this.registrationNumber;
}
public int getNumberOfSeats() {
return this.numberOfSeats;
}
}
public record Truck(int loadCapacity, String registrationNumber) implements Vehicle {
@Override
public String getRegistrationNumber() {
return this.registrationNumber;
}
public int getLoadCapacity() {
return this.loadCapacity;
}
}
```
#### Reflection
- `public boolean isSealed()`
- `public Object[] getPermittedSubclasses()`
## Removed AOT and JIT Compiler
### References
- [Baeldung - Sealed Classes](https://www.baeldung.com/java-sealed-classes-interfaces)