JAVA-B1
Klassen
public class
instance
System.out.println()
{@link Klasse}
{@param Klasse}
getClass().getSimpleName()
extends
Objekte
Interfaces
FAQ classes
Erweitern von Klassen mit extends
finale Konstanten
(@see
Finalisierung von Klassen
)
Polymorphismus
Nested Classes
@Override Annotation
innere Klassen
lokale Klassen
anonyme Klassen
Lamda Expressions
FAQ Nested Classes
Klassen im Detail
Overview
return-Statements
return & void
return bei primitiven Typen
return bei Objekten
return-Statements von Methoden mit/ohne return
Rückkehr ohne return
Rückkehr über return
throws Exception
Kopie oder Referenz
Covariant Return Types
this
this mit Objekt-Feldern
this mit Methoden und im Konstuktor
Zugriffsmodifikatoren
public protected private
Class Member
static
public static final PI
Initialisierung
Initialisierung von Feldern
Initialisierung von Klassenfeldern
Static Block
Lambda-Expressions
Methoden-Referenzen
Enumerationen und enum-Types
Enumerationskonstanten
Enumerationen mit Werten
Samstag/Sonnabend Example @!
Enumerationen FAQ
Enum @!Descr/Exmpl/Index
Interfaces
Einfache Interfaces (Marker)
Interfaces mit Methoden
Default-Methoden
static
Interfaces FAQ
Vererbung / Inheritance
Vererbung / Inheritance
super Keyword
method-overriding-and-hiding
hiding-of-fields
object-as-superclass
Finalisierung
Finale Klassen und Methoden
Finale Methoden
Finale Utility-Klasse "IdGenerator"
"Template-Methode"-Pattern
DataProcessor process(){ r(), p(), w() }
Composition-over-Inheritance
abstract
Abstrakte Klassen und Methoden
Beispiel
Abstrakte Klassen und Methoden
abstract
Implementierungsklassen
FAQ Vererbung
Kein this in super(..)
Verhindern Überschreiben von Membern
Generics
Generische Programmierung
Generische Typparameter
Generische Klassen
Generische Interfaces
Generische Methoden
Generische Felder
Generische Collection (ArrayList)
Diamond Operator
Generische Arrays
Example
Beispiel "GenerischerSpeicher"
Ring-Speicher-Algorithus mit generischem Array
Beispiel: GenerischePerson mit Berufsgruppe
Beispiel: Berufsgruppe mit Static/Non-Static Nested Classes
Modularisierung
Package
Overview
Package Structure
Package Info
Modules
Overview
Setup
Setup
Module Info
Java New Features Java 8+
Java Record
Record Syntax
Record Demo
Herkömmliche POJOs
Lösung mit Records
Records Zusammenfassung
INDEX
Records
Records | Overview
Objekte kann man in JAVA nicht nur über Klassen erzeugen sondern auch über Records, es gibt also neben «class A» auch «record A» als Alternative. Records sind für bestimmte Problemstellungen sehr praktisch.
In diesem Themenblock erläutern wir, was Records im Vergleich zu POJOs sind, wozu diese dienen, welche Vorteile Sie haben und wann man diese einsetzen sollte.
Demo
Records | Demo
Der File Demo.java beihaltet das Hauptprogramm zum Testen sowohl der NachrichtClass-Klasse als auch des NachrichtRecord-Records.
Um die Unterschiede zwischen der Verwendung der Klasse und des Records erkennen zu können, muss man schon genauer hinsehen.
package com.stuelken.java.b2.records;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Consumer;
public class Demo {
public static void main(String[] args) throws IOException {
mitKlasse();
mitRecord();
}
public static void mitKlasse() {
System.out.println("Mit Klasse:");
Collection<NachrichtClass> nachrichten = new ArrayList<NachrichtClass>();
NachrichtClass n0, n1, n2;
LocalDateTime timestamp = LocalDateTime.now();
n0 = new NachrichtClass(timestamp, "In China ist ein Sack Reis umgefallen.");
n1 = new NachrichtClass(timestamp, "Kalifornien von Aliens angegriffen.");
n2 = new NachrichtClass(timestamp, "Bernsteinzimmer bei Hempels gefunden");
nachrichten.add(n0);
nachrichten.add(n1);
nachrichten.add(n2);
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
Consumer<? super NachrichtClass> consumer = nx -> System.out
.println(nx.getMsgDatum().format(formatter) + ": " + nx.getMsgInhalt());
nachrichten.forEach(consumer);
}
public static void mitRecord() {
System.out.println("Mit Record:");
Collection<NachrichtRecord> nachrichten = new ArrayList<NachrichtRecord>();
NachrichtRecord n0, n1, n2;
LocalDateTime timestamp = LocalDateTime.now();
n0 = new NachrichtRecord(timestamp, "In China ist ein Sack Reis umgefallen.");
n1 = new NachrichtRecord(timestamp, "Kalifornien von Aliens angegriffen.");
n2 = new NachrichtRecord(timestamp, "Bernsteinzimmer bei Hempels gefunden");
nachrichten.add(n0);
nachrichten.add(n1);
nachrichten.add(n2);
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
Consumer<? super NachrichtRecord> consumer = nx -> System.out
.println(nx.msgDatum().format(formatter) + ": " + nx.msgInhalt());
nachrichten.forEach(consumer);
}
}
Erläuterungen
Record braucht keinen Getter
Da die Werte des Record final sind, werden die zugehörigen
Getter-Methoden durch Java automatisch erzeugt und tragen
den gleichen Namen wie die Komponente msgInhalt()
nur eben mit den Klammern des Funktionsoperators.
Vom Prinzip her hätte man die Methode bei der Klasse allerdings genauso bezeichnen können, nur werden Getter bei Klassen in JAVA üblicherweise mit «get» begonnen, wobei wegen camelCase-Syntax dann aber der folgende Buchstabe groß geschrieben wird.
Datum und Zeit, lokal
Ist der einfachste Weg, wie man im Programmcode mal schnell in JAVA einen Zeitstempel bekommen kann.
Um diesen Wert später auch ausgeben zu können, ist ein Formatter erforderlich.
POJO
JAVA-B1 | Herkömmliche POJOs
Um das Verhalten von Records mit herkömmlichen Klassen in etwa realisieren zu können, ist es erforderlich, das Objekt mit Hilfe des Konstruktors erzeugen zu können, dann aber für private Felder auf einen Setter zu verzichten.
package com.stuelken.java.b2.records;
import java.time.LocalDateTime;
/**
* @author t2m
*/
public class NachrichtClass {
public NachrichtClass(LocalDateTime timestamp, String msgInhalt) {
if (msgInhalt == null)
throw new IllegalArgumentException("msgInhalt darf nicht null sein. ");
if (msgInhalt.length() == 0)
throw new IllegalArgumentException(
"msgInhalt darf kein leerer String sein sondern muss mindestens 1 Zeichen haben.");
this.msgDatum = timestamp;
this.msgInhalt = msgInhalt;
}
private LocalDateTime msgDatum = null;
private String msgInhalt = "";
public LocalDateTime getMsgDatum() {
return msgDatum;
}
public String getMsgInhalt() {
return msgInhalt;
}
}
Erläuterungen
Private Felder mit Getter ohne Setter
Dadurch, dass die Felder privat sind, kann man von außen auf die zugehörigen Werte nur über den Getter zugreifen. Da der Setter fehlt, kann man den einmal über den Konstruktor gesetzten Wert nicht mehr verändern.
Werte von Argumenten prüfen
Zur Vermeidung fehlerhafter Werte werden wir Exceptions aus. Es ist sinnvoll, hier auf eine IllegalArgumentException auszuweichen.
Records
Records | Lösung mit Records
Bei einem Record wird zumeist nur der Header mit den 1 bis n Komponenten programmiert. Will man aber die Werte noch überprüfen können, ist auch für Records ein Konstrukteur möglich. Dieser hat aber eine andere Syntax als bei Klassen.
package com.stuelken.java.b2.records;
import java.time.LocalDateTime;
/**
* Die zwei Parameter im Header {@link #msgDatum} und {@link #msgInhalt()}
* bezeichnet man als <b>Komponenten</b> oder englisch Components.
*
* Objekte eines Record sind final und damit unveränderlich.
*
* @author t2m
*/
public record NachrichtRecord(LocalDateTime msgDatum, String msgInhalt) {
/**
* Ein Konstruktor einer {@link Record} benötigt keine Parameter weil die die
* Parameter bereits über Record-Header deklariert wurden und als finale
* Eigenschaften {@link #msgDatum} und {@link #msgInhalt} vorliegen.
*
* @param msgDatum
* @param msgInhalt
*/
public NachrichtRecord {
if (msgInhalt == null)
throw new IllegalArgumentException("msgInhalt darf nicht null sein. ");
if (msgInhalt.length() == 0)
throw new IllegalArgumentException(
"msgInhalt darf kein leerer String sein sondern muss mindestens 1 Zeichen haben.");
}
}
Erläuterungen
Komponenten eines Records
Die zwei Parameter im Header des Record bezeichnet man als Komponenten oder englisch Components. Dieses Record kann also nur erzeugt werden, wenn beide Komponenten vorhanden sind, wobei vom Prinzip auch null übergeben kann.
Konstuktor von Records ohne Parameter
Will man die Werte der Komponenten eines Record überprüfen, kann man auch für Records einen zugehörigen Konstruktor programmieren.
Der Konstruktor eines Record hat aber keine Parameter, weil diese als Komponenten schon im Header angegeben wurden.
Fehler auswerfen - Werte eh nicht änderbar.
Will man die Werte der Komponenten eines Record überprüfen, kann man auch für Records einen zugehörigen Konstruktor programmieren.
Die Werte der Komponenten eines Records verhalten sich finale Member und damit wie Konstanten, können also ohnehin nicht verändert oder korrigiert werden.
Ausgabe
Records | Ausgabe
Die Ausgabe des Programms.
Mit Klasse:
20XX-06-14T22:07:39.9325487: In China ist ein Sack Reis umgefallen.
20XX-06-14T22:07:39.9325487: Kalifornien von Aliens angegriffen.
20XX-06-14T22:07:39.9325487: Bernsteinzimmer bei Hempels gefunden
Mit Record:
20XX-06-14T22:07:39.9505383: In China ist ein Sack Reis umgefallen.
20XX-06-14T22:07:39.9505383: Kalifornien von Aliens angegriffen.
20XX-06-14T22:07:39.9505383: Bernsteinzimmer bei Hempels gefunden
Zusammenfassung
Records | Zusammenfassung
Immutability/Unveränderlichkeit
Alle Felder eines Record sind final. Sie lassen sich nicht verändern. Mutierende Setter gibt es bei Records nicht.
Bei POJOs sind Felder, wenn man diese zumindest über Argumente
im Konstruktor belegen möchte, immer veränderbar. Man kann
das Verändern der Werte nur über den Umweg zu finden versuchen,
dass die Einschaften private
deklariert und für den Zugriff nur eine Getter-Methode
schreibt, die Setter-Methode weglässt.
Vererbung: Superklassen
Alle Records erben von java.lang.Record.
POJOs können im Prinzip von allen Klassen erben, sofern die Superklasse eine Vererbung zulässt.
Vererbung: Erweitern
Da Records immer direkt von java.lang.Record erben, kann ein Record B einen anderen Record A nicht mit extends erweitern.
Hinweis: Der Sinn und Zweck besteht hierbei darin, dass Records «strikt wertebasiert» bleiben sollen. Keine tiefe Hierarchie, keine versteckten Instanzfelder, nur finale Felder.
Klassen für POJOs lassen sich immer mit extends
erweitern, sofern die Basisklasse nicht mit public final class
deklariert wurde.
Interfaces
Auch Records können tatsächlich Interfaces implementieren, und das auch generisch.
interface Message<C> { C content(); }
public record Nachricht<C>(C content) implements Message<C> { }
Eine Record-Komponente content erfüllt die über das Interface
vorgegebene abstrakte Methode content() automatisch, ohne
dass man zusätzlichen Code programmieren muss.
Man kann auch weitere Methoden über Interfaces vorgeben. Maßgebend ist nur, dass man die Immutability bzw. Immutabilität oder Unveränderlichkeit von Werten bzw. Komponenten des Records einhält.
Records können auch mehrere Interfaces implementieren:
interface Auditable { Instant createdAt(); }
record AuditMessage<C>(C content, Instant createdAt) implements Message<C>, Auditable { }
Klassen für POJOS können durchaus auch generisch sein und ebenso Interfaces implementieren.
Pattern Matching
if (obj instanceof Milk(var date, var days)).. ist
ein Beispiel dafür, wie man einen Typenvergleich bei Records
durchführen kann.
if (obj instanceof Message<?> m) { System.out.println(m.content()); }
Sealed + Records
Es lassen sich auch versiegelte Records realisieren: So kann man
ein sealed Interface definieren, welches
über die Angabe von permits aber
von UserCreated und OrderPaied verwendet werden kann.
sealed interface IEvent permits UserCreated, OrderPaid { String content() }
record UserCreated(UUID id) implements IEvent { }
record OrderPaid(UUID id, BigDecimal amount) implements IEvent { }
So wird erzwungen, dass beide Records das Interface implementieren dürfen, doch das Record bleibt hierbei weiterhin «final» und damit immutabel bzw. unveränderlich.
Mit Objekts ist das sinngemäß auch möglich.
FootNotes, Keywords, Tags
Hinweise, Rechte, Marken
UIO3 Es ist einfacher als Du denkst.
Stelle noch heute Deine Anfrage.
