Modernes Java bietet kompakte Mittel für Datenklassen und feste Wertemengen.
Records: Datenklassen in einer Zeile
Ein Record (ab Java 16) ist eine unveränderliche Datenklasse. Der Compiler erzeugt Konstruktor, Zugriffsmethoden, equals, hashCode und toString:
public class Aufnahme {
record Person(String name, int alter) {
Person { // kompakter Konstruktor: Prüfung
if (alter < 0) throw new IllegalArgumentException("Alter < 0");
}
String gruss() { return "Hallo, ich bin " + name; }
static Person kind(String name) { return new Person(name, 0); }
}
public static void main(String[] args) {
Person mia = new Person("Mia", 17);
System.out.println(mia); // toString automatisch
System.out.println(mia.name() + " " + mia.alter() + " " + mia.gruss());
System.out.println(mia.equals(new Person("Mia", 17)) + " " + Person.kind("Tom"));
try { new Person("X", -1); } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); }
}
}Ausgabe
Person[name=Mia, alter=17] Mia 17 Hallo, ich bin Mia true Person[name=Tom, alter=0] Alter < 0
Records eignen sich für Daten, die nur transportiert werden: Rückgabewerte, Nachrichten, Konfiguration. Sie sind flach unveränderlich (Felder final).
Enums: feste Wertemengen
Ein Enum ist ein Typ mit einer festen Menge benannter Konstanten:
public class Wochentage {
enum Tag { MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG }
enum Planet {
MERKUR(3.303e+23, 2.4397e6), ERDE(5.976e+24, 6.37814e6);
private final double masse, radius;
Planet(double masse, double radius) { this.masse = masse; this.radius = radius; }
double oberflaechengewicht(double m) { return m * 6.67300E-11 * masse / (radius * radius); }
}
public static void main(String[] args) {
Tag t = Tag.FREITAG;
System.out.println(t + " " + t.ordinal() + " " + Tag.valueOf("SONNTAG") + " " + Tag.values().length);
boolean wochenende = switch (t) { case SAMSTAG, SONNTAG -> true; default -> false; };
System.out.println(wochenende);
for (Planet p : Planet.values()) System.out.printf("%s: %.2f%n", p, p.oberflaechengewicht(70));
System.out.println(Tag.valueOf("MONTAG").compareTo(Tag.DONNERSTAG));
}
}Ausgabe
FREITAG 4 SONNTAG 7 false MERKUR: 259.21 ERDE: 686.19 -3
Enums können Felder, Konstruktoren und Methoden haben und Interfaces umsetzen. EnumMap und EnumSet sind besonders effiziente Collections für Enums.
Sealed Interfaces: geschlossene Hierarchien
Mit sealed legst du fest, welche Klassen erlaubt sind. In Kombination mit switch weiß der Compiler, dass alle Fälle abgedeckt sind:
public class Formen {
sealed interface Form permits Kreis, Rechteck, Dreieck {}
record Kreis(double r) implements Form {}
record Rechteck(double b, double h) implements Form {}
record Dreieck(double basis, double hoehe) implements Form {}
static double flaeche(Form f) {
return switch (f) { // keine default-Zeile nötig
case Kreis k -> Math.PI * k.r() * k.r();
case Rechteck r -> r.b() * r.h();
case Dreieck d -> d.basis() * d.hoehe() / 2;
};
}
public static void main(String[] args) {
for (Form f : new Form[] { new Kreis(1), new Rechteck(3, 4), new Dreieck(6, 5) })
System.out.printf("%s -> %.2f%n", f, flaeche(f));
}
}Ausgabe
Kreis[r=1.0] -> 3.14 Rechteck[b=3.0, h=4.0] -> 12.00 Dreieck[basis=6.0, hoehe=5.0] -> 15.00
Fügst du später eine weitere Form hinzu, meldet der Compiler jeden switch, der sie noch nicht behandelt.
Ergebnis oder Fehler
public class Ergebnis {
sealed interface Result<T> permits Ok, Fehler {}
record Ok<T>(T wert) implements Result<T> {}
record Fehler<T>(String nachricht) implements Result<T> {}
static Result<Integer> teile(int a, int b) {
return b == 0 ? new Fehler<>("Division durch 0") : new Ok<>(a / b);
}
public static void main(String[] args) {
for (int b : new int[] { 2, 0 }) {
String s = switch (teile(10, b)) {
case Ok<Integer> ok -> "Ergebnis " + ok.wert();
case Fehler<Integer> f -> "Fehler: " + f.nachricht();
};
System.out.println(s);
}
}
}Ausgabe
Ergebnis 5 Fehler: Division durch 0
Record, Klasse oder Enum?
| Du brauchst ... | Nimm |
|---|---|
| unveränderliche Daten mit Gleichheit nach Inhalt | record |
| Zustand und Verhalten, veränderlich | class |
| feste, endliche Wertemenge | enum |
| geschlossene Familie von Varianten | sealed interface + records |
| Fähigkeit/Vertrag | interface |
Merke
record Punkt(int x, int y) {}erzeugt Konstruktor, Getter,equals,hashCode,toString- Enums sind Typen mit festen Konstanten, mit Feldern und Methoden
sealedbegrenzt erlaubte Untertypen;switchprüft Vollständigkeit- Records und sealed Interfaces bilden Daten mit mehreren Varianten sauber ab
Aufgabe
Modelliere eine Zahlungsart als sealed Interface mit Records (Karte, PayPal, Rechnung) und eine switch-Funktion, die den Betrag mit Gebühren berechnet.