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

Destructoring ist die Zukunft von Javas Encapsu...

Avatar for Richard Richard
January 26, 2026

Destructoring ist die Zukunft von Javas Encapsulation(v1) 🇩🇪 @JUG Darmstadt 2026

Dekonstruktion – die Zerlegung eines Objekts in seine Bestandteile – ist der Schlüssel, damit Java Kapselung und Invarianten zuverlässig wahren kann. Dieses scheinbare Paradoxon lösen wir im Vortrag auf, indem wir uns vergangene und kommende Sprach- und Plattformfeatures im JDK ansehen.

Die klassische Java-Serialisierung bricht Kapselung: Sie greift direkt auf private Felder zu, umgeht damit Konstruktoren und die dort verankerten Prüfungen. Ursache ist, dass Dekonstruktion und Rekonstruktion bislang keine first-class Citizens im JDK waren.

Genau hier setzt aktuelle Arbeit im Rahmen von JDK Project Amber an – unter dem Arbeitstitel „Derived Record/Class Creation“. Der Ansatz erlaubt es, explizit festzulegen, wie Objekte de- und rekonstruiert werden. Damit lässt sich künftig nicht nur Serialisierung sicherer und robuster gestalten; wir gewinnen auch „Withers“ (zielgerichtete, unveränderliche Kopien mit einzelnen geänderten Komponenten) für Records sowie Pattern Matching, das über reine Record-Patterns hinaus auf normale Klassen erweitert wird.

Unter diesen Voraussetzungen lohnt sich ein genauer Blick: Was ist schon da, was kommt als Nächstes – und was bedeutet das für unsere tägliche Java-Praxis?

Avatar for Richard

Richard

January 26, 2026
Tweet

More Decks by Richard

Other Decks in Programming

Transcript

  1. 22.01.26 Richard Gross (he/him) Software Archaeology richargh.de richargh.de/ richargh Hypermedia

    Modernisation Destructoring ist die Zukunft von Javas Encapsulation
  2. JDK Deserialisierung Der Code Die Deserialisierung mit der Api 1.

    public class Adult { 2. private final Instant birthdate; 3. 4. public Adult(Instant birthdate) { 5. this.birthdate = birthdate; 6. if(isYoungerThan18(birthdate)) 7. throw new Ex(“Is not 18 yet.”); 8. } 9. 10. public long age(){ /** **/ } 11. } 1. // deserialisieren 2. var adult = (Adult) objectInputStream 3. .readObject(); 4. 5. IO.println(adult.age()); // prints 17??? Slide 3 CC BY-SA richargh.de
  3. Third-Party Deserialisierung Der Code Die Deserialisierung mit Jackson 1. public

    class Adult { 2. private final Instant birthdate = null; 3. 4. public Adult() { } 5. 6. public Adult(Instant birthdate) { 7. this.birthdate = birthdate; 8. if(isYoungerThan18(birthdate)) 9. throw new Ex(“Is not 18 yet.”); 10. } 11. 12. public Instant getBirthdate() { 13. return this.birthdate; 14. } 15. 16. public long age(){ /** **/ } 17. } 1. // deserialisieren 2. var result = objectMapper 3. .readValue(json, Adult.class); 4. 5. IO.println(adult.age()); // prints 17??? Slide 5 CC BY-SA richargh.de
  4. Encapsulation • Ein Element garantiert, dass es immer in einem

    erlaubten Zustand ist • Invarianten beschreiben was immer gültig ist 7
  5. Wie man Encapsulation bricht Serialization Api • Serialisieren: alle privaten

    Felder aufsaugen • Private, final, kann man doch alles ignorieren • Deserialisieren: alle privaten und finalen Felder setzen, Constructor ignorieren Reflection Api (analog) Slide 8 CC BY-SA richargh.de 1. var adult = new Adult(...); 2. // Feld per reflection 3. Field birthdateField = adult.getClass() 4. .getDeclaredField("birthdate"); 5. birthdateField.setAccessible(true); 6. birthdateField.set(adult, seventeenYearsAgo); 7. // ^^^ 8. // deep reflection (seit JDK 5) 9. // Warnung mit JDK 26 (März 2026)
  6. Integrity by default1 Developers expect that their code and data

    is protected against use that is unwanted or unwise. (…) Going forward, we will restrict unsafe APIs so that, by default, libraries, frameworks, and tools cannot use them2. 1 https://openjdk.org/jeps/8305968 2 https://openjdk.org/jeps/500 Slide 9 CC BY-SA richargh.de
  7. Integrity by default1 Application authors will have the ability to

    override this default. 1 https://openjdk.org/jeps/8305968 Slide 10 CC BY-SA richargh.de
  8. Derzeit können wir dem gelesenen Code nicht vertrauen Zur Laufzeit

    passiert dank deep reflection etwas ganz anderes Slide 11 CC BY-SA richargh.de
  9. Wir brauchen (De-)Serialisierung Slide 12 CC BY-SA richargh.de System Binary

    ObjectStream Protobuf Avro BSON RESP Xml Json Yaml Toml Html Csv docx Java Object Binary ObjectStream Protobuf Avro BSON RESP Xml Json Yaml Toml Html Csv docx Hibernate Jackson Protobuf Hibernate Jackson Protobuf
  10. Die bisherigen Apis sind nicht gerade einfach Serialization Api Third-Party

    Serialization 1. readObject 2. writeObject 3. readObjectNoData 4. readResolve 5. writeReplace 6. serialVersionUID 7. serialPersistantFields Slide 13 CC BY-SA richargh.de 1. @JsonAnyGetter 2. @JsonAnySetter 3. @JsonGetter 4. @JsonSetter 5. @JsonValue 6. @JsonRawValue 7. @JsonSerialize 8. @JsonDeserialize 9. @JsonCreator 10. @JsonAlias 11. @JsonIgnoreProperties 12. @JsonIgnore 13. @JsonIgnoreType 14. @JsonInclude 15. @JsonIncludeProperties 16. etc.
  11. Es fehlt ein Konzept im JDK Slide 15 CC BY-SA

    richargh.de System Java Object Constructor Deconstructor Deserialisieren Serialisieren Noch kein first- class Citizen
  12. Deconstruction als first-class citizen 2026-01 Carrier Classes https://mail.openjdk.org/pipermail/amber-spec-experts/2026-January/004307.html 2025-03 Where

    Is the Java Language going https://www.youtube.com/watch?v=1dY57CDxR14 2024-10 Serialization a new Hope https://www.youtube.com/watch?v=fbqAyRJoQO0 2019-06 Towards better Serialization https://openjdk.org/projects/amber/design-notes/towards-better-serialization public class Point { private final int x; private final int y; // Constructor public Point(int x, int y) { this.x = x; this.y = y; } // Deconstruction pattern public pattern Point(int x, int y) { x = this.x; y = this.y; } } Slide 16 CC BY-SA richargh.de Strawman syntax Viele Syntax-Ideen stehen im Raum Syntaktisch der inverse Constructor
  13. Zwei-Attribut (Third-Party) Serialisation Api public class Point { private final

    int x; private final int y; @Deserializer public Point(int x, int y) { this.x = x; this.y = y; } @Serializer public pattern Point(int x, int y) { x = this.x; y = this.y; } } Slide 18 CC BY-SA richargh.de
  14. Serialisation Api mit Versionssupport public class Point { private final

    int x; private final int y; @Deserializer(version = 2) public Point(int x, int y) { this.x = x; this.y = y; } @Deserializer(version = 1) public Point(int x) { this(x, 0); } @Serializer(version = 2) public pattern Point(int x, int y) { x = this.x; y = this.y; } } Slide 19 CC BY-SA richargh.de
  15. Pattern matching für Klassen Deconstruction definieren Und matchen 1. public

    class Address { 2. 3. private final String street; 4. private final String city; 5. /* Constructor etc. */ 6. 7. // deconstruction pattern 8. public pattern Address(int street, int city) { 9. street = this.street; 10. city = this.city; 11. } 12. } Slide 20 CC BY-SA richargh.de 1. void handle(Object obj) { 2. if(obj instanceOf Address(var street, var city)){ 3. // do something with street and city 4. } 5. }
  16. Pattern matching für Factories Von verboser Deconstruction Zum kompakten Matchen

    1. // construction 2. Optional<Shape> maybeShape = Optional 3. .of(Ball.of(RED, 1)); 4. // verbose deconstruction by hand 5. Shape s = maybeShape.orElse(null); 6. if(s != null 7. && s.isBall() 8. && (s.color() == RED)){ 9. var ball = (Ball) s; 10. IO.printLn(ball.size() + " red balls"); 11. } Slide 21 CC BY-SA richargh.de 1. // construction 2. var shape? = Optional.of(Ball.of(RED, 1)); 3. // compact matcher 4. if(shape? instanceOf Optional.of(Ball.of(RED, var count))){ 5. IO.printLn(count + " red balls"); 6. }
  17. Class Withers Mit neuer Syntax Klassen rekonstruieren 1. // strawman

    canonical constructor 2. class Address(String street, String city) { 3. 4. // strawman assignment syntax 5. private final String street = street; 6. private final String city = city; 7. 8. // strawman compact constructor 9. Address { 10. notBlank(street); 11. notBlank(city); 12. } 13. 14. // deconstruction pattern 15. pattern Address(int street, int city) { 16. street = this.street; 17. city = this.city; 18. } 19. } Slide 22 CC BY-SA richargh.de 1. var newAddress = anAddress with { 2. street = "Cool Blvd"; 3. city = "Cool City"; 4. }
  18. Vom manuellen with mit Records Jedes with manuell schreiben Records

    manuell rekonstruieren 1. record Address(String street, String city){ 2. Address { 3. notBlank(street); 4. notBlank(city); 5. } 6. 7. Address withStreet(String street){ 8. return new Address(street, this.city); 9. } 10. 11. Address withCity(String city){ 12. return new Address(this.street, city); 13. } 14. } Slide 23 CC BY-SA richargh.de 1. var newAddress = anAddress 2. .withStreet("Cool Blvd”) 3. .withCity("Cool City”);
  19. Zu manuellen with zu Record Withers1 1 aka Derived Record

    Creation https://openjdk.org/jeps/468 Kein with mehr schrieben Records automatisch rekonstruieren 1. record Address(String street, String city){ 2. Address { 3. notBlank(street); 4. notBlank(city); 5. } 6. } Slide 24 CC BY-SA richargh.de 1. var newAddress = anAddress with { 2. street = "Cool Blvd"; 3. city = "Cool City"; 4. }
  20. Fragen? richargh.de Richard Gross (he/him) Software Archaeology Hypermedia Modernisation Works

    for maibornwolff.de/ richargh.de richargh Slide 26 CC BY-SA richargh.de https://content.maibornwolff.de/meetings/richard-gross Trink ein (virtuelles) Heißgetränk mit mir. * Alle Code Beispiele https://github.com/Richargh/encapsulation-java-mvn-sandbox
  21. Jackson ohne Default-Constructor See https://github.com/Richargh/encapsulation-java-mvn- sandbox/blob/trunk/src/test/java/de/richargh/sandbox/encapsulation/jackson/JacksonClassMapperTest.java#L1 90 Klasse mit einem

    Constructor Mit ParameterNamesModule und CompilerFlag gehts ohne no-args 1. public class Address { 2. 3. private final String street; 4. private final String city; 5. 6. // constructor for deserialization 7. public Address(String street, String city){ 8. this.street = Validate.notBlank(street); 9. this.city = Validate.notBlank(city); 10. } 11. 12. // two getters for serialization 13. public String getStreet(){ return this.street; } 14. public String getCity(){ return this.city; } 15. } 16. Slide 28 CC BY-SA richargh.de 1. // Requires ParameterNamesModule in Jackson 2.x 2. // and the compilerArg: -parameters. 3. var mapper = JsonMapper.builder() 4. .addModule(new ParameterNamesModule()) 5. .build(); 6. var result = objectMapper.readValue(json, Address.class);