Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Explore Java 17 and beyond

Explore Java 17 and beyond

Features released in Java in the last year have brought greater opportunities for developers to improve application readability, maintainability as well as development productivity.
In this hands-on lab, you will have a chance to discover Java 17 and use some of the most recent Project Amber language features added to the Java platform such as Records, Switch Expression, Pattern Matching for instanceof, Pattern Matching for switch (as a preview feature), Sealed Classes, ..., as well as get a chance to understand strong encapsulation.
Further, you will test-drive some of the new JDK 17 features such as Context-Specific Deserialization Filters.
This is an exploration lab with a bunch of exercises. Some of them need to be executed one after the other, others are completely independant, allowing the people to pick what they are most interested in. This lab has been developed for the release of Java 17, used during several online event, and will be updated for 18.

F784a8de872a5d98f67ac5ccbd6683d0?s=128

José

May 14, 2022
Tweet

More Decks by José

Other Decks in Programming

Transcript

  1. Explore Java 17 Records, Sealed Classes and Pattern Matching José

    Paumard @JosePaumard Java Developer Advocate Java Platform Group
  2. https://twitter.com/JosePaumard https://github.com/JosePaumard https://www.youtube.com/user/java https://www.youtube.com/user/JPaumard https://www.youtube.com/hashtag/jepcafe https://fr.slideshare.net/jpaumard https://www.pluralsight.com/authors/jose-paumard https://dev.java

  3. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 3

    Dev.java Java 8 Java 11
  4. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 4 Records
  5. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 5

    https://github.com/JosePaumard/ 2022_DevNexus-lab
  6. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 6

    Short answer: a class! Less short answer: a class - That extends the Record class - That is final What is a Record?
  7. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 7

    And you can write: I Need to Write a Record! public record User(String name, int age) { } User sarah = new User("Sarah", 23); System.out.println(sarah); $ User[name=Sarah, age=23]
  8. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 8

    1) A constructor 2) Accessors: name() and age() 3) The following methods: toString(), equals(), hashCode() What Do you Get with a Record? public record User(String name, int age) { }
  9. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 9

    You cannot extend a record A record cannot extend anything You cannot create any instance field in a record You can implement interfaces You can add any instance method You can add any static field or method Restrictions on Records
  10. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 10

    name and age are the components of the record Components are both final instance fields And accessors Components of a Record public record User(String name, int age) { }
  11. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 11

    More on Components public record User(String name, int age) { } User user = new User("Sarah", 23); Class<User> userClass = User.class; boolean isRecord = userClass.isRecord(); Component[] components = userClass.getRecordComponents();
  12. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 12

    More on Components public record User(String name, int age) { } User user = new User("Sarah", 23); RecordComponent nameComponent = components[0]; Class<?> declaring = nameComponent.getDeclaringRecord(); String name = (String) nameComponent.getAccessor()invoke(user);
  13. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 13

    More on Components public record User(@UserName String name, int age) { } @Target(ElementType.RECORD_COMPONENT) @interface UserName {}
  14. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 14

    The state of a record is entirely defined by its components - You cannot add more state by extension, or by extending anything - You cannot add any instance field Record State
  15. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 15

    The constructor of a record that takes its components is called the canonical constructor Record Building public record User(String name, int age) { public record User(String name, int age) { this.name = name; this.age = age; } }
  16. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 16

    Why would you write your own canonical constructor? 1) Defensive copy Record Building public record State(List<City> cities) { public State(List<City> cities) { this.cities = List.copyOf(cities); } }
  17. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 17

    In that case, you need to write your own accessor too! Record Building public record State(List<City> cities) { public State(List<City> cities) { this.cities = List.copyOf(cities); } public List<City> cities() { return List.copyOf(cities); } }
  18. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 18

    Why would you write your own canonical constructor? 2) Validation Record Building public record Range(int begin, int end) { public Range(int begin, int end) { if (begin > end) { throw new IllegalArgumentException("..."); } this.begin = begin; this.end = end; } }
  19. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 19

    Why would you write your own canonical constructor? 2) Validation, with the Compact Constructor Record Building public record Range(int begin, int end) { public Range { if (begin > end) { throw new IllegalArgumentException("..."); } } }
  20. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 20

    A record constructor must call the canonical constructor Record Building public record Range(int begin, int end) { public Range(int end) { this.begin = 0; this.end = end; } }
  21. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 21

    A record constructor must call the canonical constructor Record Building public record Range(int begin, int end) { public Range(int end) { this(0, end); } }
  22. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 22

    You cannot build a record without calling its canonical constructor Deserialization calls the canonical constructor of a record
  23. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 23

    Deserialization calls the canonical constructor of a record Records are a perfect choice for Data Transport Objects Record Serialization
  24. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 24 Sealed Types
  25. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 25

    Short answer: type (interface, class, abstract class)! Less short answer: a type - That declares its subtypes - That lives in the same package or module as them What is a Sealed Type?
  26. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 26

    A sealed type must declare its extensions Syntax public sealed interface Shape permits Square, Rectangle, WeirdShape { }
  27. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 27

    An extension of a sealed type must be: - final - or sealed - or non-sealed Syntax
  28. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 28

    An extension of a sealed type must be: - final Syntax public record Square(int edge) implements Shape { }
  29. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 29

    An extension of a sealed type must be: - sealed Syntax public sealed class Rectangle permits ExtendedRectangle { }
  30. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 30

    An extension of a sealed type must be: - non-sealed Syntax public non-sealed abstract class WeirdShape { }
  31. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 31 Pattern Matching
  32. ?Record and Array Pattern Matching? Record Sealed Classes Switch Expression

    Constant Dynamic Inner Classes private in VM Nestmates Pattern Matching for instanceof 11 14 16 17 Switch on Types 19
  33. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 33

    In reference to the Amber Chronicles by Roger Zelazny Project Amber
  34. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 34

    A pattern is a combination of a match predicate that determines if the pattern matches a target, along with a set of pattern variables that are conditionally extracted if the pattern matches the target. Java 16 : Pattern Match for IntanceOf Pattern Matching for Java Gavin Bierman and Brian Goetz, September 2018 https://github.com/openjdk/amber-docs/blob/master/site/design-notes/pattern-matching-for-java.md
  35. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 35

    String name = the type of the pattern String = the name of the pattern name = the binding variable o = target operand Type Pattern if (o instanceof String name) { // some code }
  36. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 36

    Type Pattern if (o instanceof String name) { Sytem.out.println(name.toUpperCase()); } if (o instanceof String name && name.length() > 0) { Sytem.out.println(name.toUpperCase()); }
  37. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 37

    Type Pattern if (o instanceof String name) { Sytem.out.println(name.toUpperCase()); } if (!(o instanceof String name)) { // you cant use name here! } Sytem.out.println(name.toUpperCase());
  38. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 38 Pattern Matching for Switch
  39. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 39

    Pattern Matching for Switch interface Shape {} record Square(int edge) implements Shape {} record Circle(int radius) implements Shape {}
  40. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 40

    Pattern Matching for Switch double area(Shape shape) { return switch (shape) { case Square square -> square.edge()*square.edge(); case Circle circle -> Math.PI*circle.radius()* circle.radius(); default -> 0; } }
  41. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 41

    Switch on a Sealed Type sealed interface Shape permits Square, Circle {} record Square(int edge) implements Shape {} record Circle(int radius) implements Shape {}
  42. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 42

    Switch on a Sealed Type double area(Shape shape) { return switch (shape) { case Square square -> square.edge()*square.edge(); case Circle circle -> Math.PI*circle.radius()* circle.radius(); // default -> 0; // Not needed! } }
  43. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 43

    The compiler can: - check if a type is not present and raise an error - raise a warning if an uneeded default is present Switch on a Sealed Type
  44. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 44

    Guarded Pattern (prev. 17 and 18) double area(Shape shape) { return switch (shape) { case Square square && square.edge() == 0 -> 0; case Square square -> square.edge()*square.edge(); case Circle circle -> Math.PI*circle.radius()* circle.radius(); } }
  45. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 45

    Guarded Pattern (prev. 19) double area(Shape shape) { return switch (shape) { case Square square when square.edge() == 0 -> 0; case Square square -> square.edge()*square.edge(); case Circle circle -> Math.PI*circle.radius()* circle.radius(); } }
  46. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 46

    (19 prev) Switch and Record Pattern Matching double area(Shape shape) { return switch (shape) { case Square(int edge) when edge == 0 -> 0; case Square(int edge) -> edge*edge; case Circle(int radius) -> Math.PI*radius*radius; } }
  47. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 47 Record Pattern Matching (prev Java 19)
  48. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 48

    Record Pattern Matching record User(String name, int age) {} if (o instanceof User user)) { String name = user.name(); int age = user.age(); }
  49. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 49

    name and age are initialized by calling the record accessors So defensive copy is possible Record Pattern Matching record User(String name, int age) {} if (o instanceof User(String name, int age))) { // use name and age }
  50. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 50

    Record Pattern Matching record User(String name, int age) {} if (o instanceof User(String name, int age) user)) { // use name, age and user }
  51. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 51 Array Pattern Matching
  52. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 52

    Array Pattern Matching if (o instanceof String[] array && array.length() >= 2) { // do something with array[0] and array[1] }
  53. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 53

    Array Pattern Matching if (o instanceof String[] {s1, s2}) { // do something with s1 and s2 }
  54. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 54

    Nesting Patterns + Type Inference if (o instanceof Circle[] {Circle(var r1), Circle(var r2)}) { // do something with r1 and r2 } record Circle(int radius) {}
  55. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 55

    Nesting Patterns + Type Inference if (o instanceof Circle[] { Circle(Point(int x1, int y1), _), Circle(Point(int x2, int y2), _)}) { // do something with x1, y1, x2, y2 } record Circle(Point center, int radius) {} record Point(int x, int y) {}
  56. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 56

    Nesting Patterns + Type Inference List<Point> points = ...; for (Point point: points) { int x = point.x(); int y = point.x(); // do something with x and y } record Point(int x, int y) {}
  57. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 57

    Nesting Patterns + Type Inference List<Point> points = ...; for (Point(int x, int y): points) { // do something with x and y } record Point(int x, int y) {}
  58. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 58 The Future of Pattern Matching
  59. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 59

    Nested Patterns, needed binding variables, type inference Syntaxic Sugars if (shape instanceof Circle(var center, _) && center instanceof Point(int x, int y)) { // center and radius are binding variables } if (shape instanceof Circle(Point(int x, int y), _)) { // center and radius are binding variables }
  60. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 60

    The deconstruction uses the canonical constructor of a record What about: - factory methods? Deconstruction
  61. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 61

    Deconstruction Using Factory Methods interface Shape { static Circle circle(double radius) { return new Circle(radius); } static Square square(double edge) { return new Square(edge); } } record Circle(double radius) {} record Square(double edge) {}
  62. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 62

    Then this code could become possible: Deconstruction Using Factory Methods double area = switch(shape) { case Shape.circle(double radius) -> Math.PI*radius*radius; case Shape.square(double edge) -> edge*edge; }
  63. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 63

    With factory methods Deconstruction Using Factory Methods if (opt instanceof Optional.of(var max)) { // max is a binding variable }
  64. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 64

    With factory methods Deconstruction Using Factory Methods if (s instanceof String.format("%s is %d years old", String name, int age)) { // name and age are binding variables }
  65. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 65

    Map deconstruction This is an extended form of Pattern Matching where you check the value of a binding variable Deconstruction Using Factory Methods if (map instanceof Map.withMapping("name", var name) && map instanceof Map.withMapping("email", var email)) { // name and email are binding variables }
  66. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 66

    Pattern combination More Examples if (map instanceof Map.withMapping("name", var name) __AND Map.withMapping("email", var email)) { // name and email are binding variables } __AND = pattern combination
  67. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 67

    More Examples { "firstName": "John", "lastName": "Smith", "age": 25, "address" : { "street": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" } } if (json instanceof stringKey("firstName", var firstName) __AND stringKey("lastName", var lastName) __AND intKey("age", var age) __AND objectKey("address", stringKey("street", var street) __AND stringKey("city", var city) __AND stringKey("state", var state) )) { // firstName, lastName, age, // street, city, state, ... // are binding variables }
  68. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 68

    If Java Embraces « Map Literals » Map<String, String> map = { "firstName": "John", "lastName": "Smith", "age": "25" } if (map instanceof { "firstName": var firstName, "lastName": var lastName, "age": Integer.toString(var age) }) { // firstName, lastName, age // are binding variables // age is a deconstructed int }
  69. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 69

    The deconstruction uses the canonical constructor of a record What about: - factory methods? - classes that are not records? Deconstruction
  70. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 70

    What About Your POJOs? public class Point { private int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public deconstructor(int x, int y) { x = this.x; y = this.y; } } This is the output of the deconstructor It allows both: - overloading - defensive copy
  71. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 71

    Match can check if an expression matches a pattern Just as if checks for a boolean expression Introducing Match
  72. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 72

    Pattern with Match record Point(int x, int y) {} record Circle(Point center, int radius) implements Shape {} Circle circle = ...; match Circle(var center, var radius) = circle; // center and radius are binding variables
  73. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 73

    Pattern with Match record Point(int x, int y) {} record Circle(Point center, int radius) implements Shape {} Circle circle = ...; match Circle(var center, var radius) = circle else throw new IllegalStateException("Not a circle");
  74. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 74

    Pattern with Match record Point(int x, int y) {} record Circle(Point center, int radius) implements Shape {} Circle circle = ...; match Circle(var center, var radius) = circle else { center = new Point(0, 0); // this is called radius = 0; // an anonymous matcher }
  75. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 75

    You can use match with more than one pattern… … or use nested patterns Pattern with Match Shape shape = ...; match Rectangle(var p1, var p2) = shape, Point(var x0, var y0) = p1, Point(var x1, var y2) = p2;
  76. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 76

    • Constant Patterns: checks the operand with a constant value • Type Patterns: checks if the operand has the right type, casts it, and creates a binding variable Patterns at a Glance
  77. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 77

    • Patterns + Deconstruction: checks the operand type, casts it, bind the component to binding variables • Patterns + Method: uses a factory method or a deconstructor • Patterns + Var: infers the right type, and creates the binding variable • Pattern + _: infers the right type, but does not create the binding variable Patterns at a Glance
  78. 5/13/2022 Copyright © 2021, Oracle and/or its affiliates | 78

    Where are we? • Pattern Matching for instanceof (16) • Pattern Matching for Switch (prev 17, 18, 19) • Record Pattern Matching (19) • Array Pattern Matching • Match • Literals Patterns at a Glance