## 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)