From ee55b2dfa303f6940454d82085f97d0263c3ab28 Mon Sep 17 00:00:00 2001 From: "daniel.appenmaier@gmail.com" Date: Wed, 24 Jun 2026 11:32:20 +0200 Subject: [PATCH 1/2] Small Fixes and Improvements by Claude --- docs/additional-material/tools/debugging.mdx | 38 ++++++++-------- docs/documentation/algorithms.mdx | 24 +++------- docs/documentation/arrays.md | 37 +++++++++++++--- docs/documentation/binary-numbers.mdx | 2 +- docs/documentation/class-structure.mdx | 18 ++++---- docs/documentation/data-objects.md | 4 +- docs/documentation/exceptions.mdx | 45 +++++++++++++++++++ docs/documentation/hashing.mdx | 46 ++++++++++++++++++++ docs/documentation/interfaces.mdx | 10 ++++- docs/documentation/java-stream-api.md | 2 +- docs/documentation/lambdas.md | 29 +++++++++--- docs/documentation/object.md | 9 ++++ docs/documentation/trees.md | 30 +++++++++++++ 13 files changed, 227 insertions(+), 67 deletions(-) diff --git a/docs/additional-material/tools/debugging.mdx b/docs/additional-material/tools/debugging.mdx index 3d5c9d8e79..2024db4f9e 100644 --- a/docs/additional-material/tools/debugging.mdx +++ b/docs/additional-material/tools/debugging.mdx @@ -18,17 +18,15 @@ Laufzeit- und logische Fehler den Einsatz eines _Debuggers_. Ein Debugger ermöglicht es, ein Programm kontrolliert auszuführen, an beliebigen Stellen anzuhalten und den Zustand von Variablen sowie den Programmfluss zu -inspizieren. Moderne IDEs wie [Eclipse](https://eclipseide.org/) oder -[IntelliJ IDEA](https://www.jetbrains.com/idea/) verfügen über einen -integrierten Debugger. +inspizieren. [Eclipse](https://eclipseide.org/) verfügt über einen integrierten +Debugger. ## Weiterführende Links Die folgenden Ressourcen bieten eine vertiefte Einführung in das Debugging mit -den gängigsten Java-IDEs. +Eclipse. - [Eclipse Debugging Guide](https://www.eclipse.org/community/eclipse_newsletter/2017/june/article1.php) -- [IntelliJ IDEA – Debug Code](https://www.jetbrains.com/help/idea/debugging-code.html) - [Visualizing Execution with Java Visualizer](https://pythontutor.com/java.html) ## Breakpoints @@ -74,10 +72,10 @@ zu verfolgen. -**Step Over** (`F6` in Eclipse / `F8` in IntelliJ) führt die aktuelle Zeile -vollständig aus und hält in der nächsten Zeile derselben Methode an. -Methodenaufrufe werden dabei als ein Schritt behandelt – der Debugger springt -nicht in die aufgerufene Methode hinein. +**Step Over** (`F6`) führt die aktuelle Zeile vollständig aus und hält in der +nächsten Zeile derselben Methode an. Methodenaufrufe werden dabei als ein +Schritt behandelt – der Debugger springt nicht in die aufgerufene Methode +hinein. ```java title="Beispiel" showLineNumbers int a = 5; // <- Debugger hält hier an @@ -88,9 +86,8 @@ int c = b * 2; // <- Debugger hält hier an nach Step Over -**Step Into** (`F5` in Eclipse / `F7` in IntelliJ) springt in den Rumpf des -Methodenaufrufs in der aktuellen Zeile hinein, sodass dessen Ausführung Schritt -für Schritt verfolgt werden kann. +**Step Into** (`F5`) springt in den Rumpf des Methodenaufrufs in der aktuellen +Zeile hinein, sodass dessen Ausführung Schritt für Schritt verfolgt werden kann. ```java title="Beispiel" showLineNumbers int a = 5; // <- Debugger hält hier an @@ -101,16 +98,16 @@ int c = b * 2; -**Step Return** (`F7` in Eclipse / `Shift+F8` in IntelliJ) führt die restliche -aktuelle Methode vollständig aus und hält nach der Rückkehr in der aufrufenden -Methode an. Dieser Befehl wird typischerweise eingesetzt, wenn man versehentlich -in eine Methode hineingesprungen ist. +**Step Return** (`F7`) führt die restliche aktuelle Methode vollständig aus und +hält nach der Rückkehr in der aufrufenden Methode an. Dieser Befehl wird +typischerweise eingesetzt, wenn man versehentlich in eine Methode +hineingesprungen ist. -**Resume** (`F8` in Eclipse / `F9` in IntelliJ) setzt die Programmausführung -fort, bis der nächste Breakpoint erreicht wird oder das Programm regulär endet. +**Resume** (`F8`) setzt die Programmausführung fort, bis der nächste Breakpoint +erreicht wird oder das Programm regulär endet. @@ -122,15 +119,14 @@ angegebene Bedingung wahr ist. Das ist besonders nützlich, wenn ein Fehler nur unter bestimmten Umständen auftritt – etwa bei einem konkreten Schleifendurchlauf oder einem bestimmten Parameterwert. -```java title="Example: Fehler tritt nur bei i == 42 auf" showLineNumbers +```java title="Beispiel: Fehler tritt nur bei i == 42 auf" showLineNumbers for (int i = 0; i < 100; i++) { process(i); // <- bedingter Breakpoint: i == 42 } ``` In Eclipse wird ein bedingter Breakpoint über **Rechtsklick auf den Breakpoint → -Breakpoint Properties → Enable Condition** gesetzt. In IntelliJ IDEA über -**Rechtsklick auf den Breakpoint → More → Condition**. +Breakpoint Properties → Enable Condition** gesetzt. ## Häufige Fehler und deren Ursachen diff --git a/docs/documentation/algorithms.mdx b/docs/documentation/algorithms.mdx index aec2dd0c9f..7c69b4d321 100644 --- a/docs/documentation/algorithms.mdx +++ b/docs/documentation/algorithms.mdx @@ -134,15 +134,9 @@ Im nachfolgenden Beispiel wird die Zahlenfolge :::info -l = linke Grenze, r = rechte Grenze, m = Mitte - -::: - -:::info - -Bei der Ermittlung der Mitte wird i.d.R. die Abrundungsfunktion verwendet, d.h. -zu einer reellen Zahl wird die größte ganze Zahl, die kleiner als die reelle -Zahl ist, verwendet. +l = linke Grenze, r = rechte Grenze, m = Mitte. Bei der Ermittlung der Mitte +wird die Abrundungsfunktion verwendet, d.h. es wird die größte ganze Zahl +kleiner als die reelle Zahl verwendet. ::: @@ -290,15 +284,9 @@ auf beide Teile angewendet (Teile-und-Herrsche-Prinzip). :::info l = linke Grenze, r = rechte Grenze, m = Mitte, d = Datensammlung, i = linker -Index, j = rechter Index - -::: - -:::info - -Bei der Ermittlung der Mitte wird i.d.R. die Abrundungsfunktion verwendet, d.h. -zu einer reellen Zahl wird die größte ganze Zahl, die kleiner als die reelle -Zahl ist, verwendet. +Index, j = rechter Index. Bei der Ermittlung der Mitte wird die +Abrundungsfunktion verwendet, d.h. es wird die größte ganze Zahl kleiner als die +reelle Zahl verwendet. ::: diff --git a/docs/documentation/arrays.md b/docs/documentation/arrays.md index 78c6411737..7764d462af 100644 --- a/docs/documentation/arrays.md +++ b/docs/documentation/arrays.md @@ -61,16 +61,10 @@ public class MainClass { } ``` -:::info - -Der Index beginnt in Java bei 0. - -::: - ## Der Parameter _String[] args_ Der Parameter `String[] args` der main-Methode ermöglicht es, der Anwendung beim -Aufruf über die Kommandozeile Argumente zu übergeben. +Aufruf über die [Kommandozeile](java) Argumente zu übergeben. ```java title="MainClass.java" showLineNumbers public class MainClass { @@ -113,3 +107,32 @@ public class MainClass { Technisch gesehen handelt es sich bei einer variablen Argumentliste um ein Feld. ::: + +## Mehrdimensionale Felder + +Mehrdimensionale Felder sind Felder, deren Elemente selbst wieder Felder sind. +Der häufigste Anwendungsfall ist das zweidimensionale Feld, das sich als Matrix +mit Zeilen und Spalten vorstellen lässt. Der Zugriff auf ein Element erfolgt +über zwei Indizes: zuerst die Zeile, dann die Spalte. + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + int[][] matrix = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + + // alle Elemente zeilenweise ausgeben + for (int row = 0; row < matrix.length; row++) { + for (int col = 0; col < matrix[row].length; col++) { + System.out.print(matrix[row][col] + " "); + } + System.out.println(); + } + } + +} +``` diff --git a/docs/documentation/binary-numbers.mdx b/docs/documentation/binary-numbers.mdx index 9f48687097..bea5fa7d3e 100644 --- a/docs/documentation/binary-numbers.mdx +++ b/docs/documentation/binary-numbers.mdx @@ -12,7 +12,7 @@ Informationen wie Zahlen und Zeichen werden im Computer in Form von sogenannten _Bits_ (Binary Digits) gespeichert. Ein Bit ist die kleinstmögliche Informationseinheit und kann zwei Zustände annehmen: 0 und 1 (bzw. „an" und „aus", „wahr" und „falsch", „Strom fließt" und „Strom fließt nicht"). Zahlen, -die mit Hilfe von Bits dargestellt werden, nennt man _Binärzahlen_, das +die mithilfe von Bits dargestellt werden, nennt man _Binärzahlen_, das dazugehörige Zahlensystem _Binärsystem_. Zahlensysteme legen fest, wie Zahlen dargestellt werden — der Name verrät dabei jeweils, wie viele Ziffern zur Verfügung stehen. Um Verwechslungen zu vermeiden, können Zahlen mit einem nach- diff --git a/docs/documentation/class-structure.mdx b/docs/documentation/class-structure.mdx index 2f5b17c459..a5da936d64 100644 --- a/docs/documentation/class-structure.mdx +++ b/docs/documentation/class-structure.mdx @@ -8,9 +8,9 @@ tags: [class-structure] import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Klassen stellen den grundlegenden Rahmen für Programme dar. Jede Klasse kann -Daten (_Attribute_) und Routinen (_Methoden_) besitzen. Routinen bestehen aus -Folgen von verzweigten und sich wiederholenden Anweisungen, wobei Anweisungen +Klassen sind der grundlegende Rahmen für Programme. Jede Klasse kann Daten +(_Attribute_) und Routinen (_Methoden_) besitzen. Routinen bestehen aus Folgen +von verzweigten und sich wiederholenden Anweisungen, wobei Anweisungen wohldefinierte Befehle darstellen, die der Interpreter zur Laufzeit ausführt. Anweisungen müssen in Java mit einem Semikolon abgeschlossen werden und können zu Anweisungsblöcken zusammengefasst werden, die durch geschweifte Klammern @@ -103,10 +103,10 @@ bezeichnet. ## Die main-Methode Die Methode `void main(args: String[])` ist eine spezielle Methode in Java und -stellt Startpunkt sowie Endpunkt einer Anwendung bzw. eines Programms dar. Nur -Klassen mit einer main-Methode können von der Laufzeitumgebung ausgeführt -werden. Aus diesem Grund werden Klassen mit einer main-Methode auch als -_ausführbare Klassen_ oder als _Startklassen_ bezeichnet. +ist Startpunkt sowie Endpunkt einer Anwendung. Nur Klassen mit einer +main-Methode können von der Laufzeitumgebung ausgeführt werden. Aus diesem Grund +werden Klassen mit einer main-Methode auch als _ausführbare Klassen_ oder als +_Startklassen_ bezeichnet. ```java title="MainClass.java" showLineNumbers public class MainClass { @@ -162,8 +162,8 @@ Einsatz von Quellcode-Kommentaren i.d.R. verzichtet werden sollte. ## Entwicklungspakete Entwicklungspakete ermöglichen das hierarchische Strukturieren von Klassen. Um -die Klassen eines Entwicklungspaketes verwenden zu können, müssen die jeweiligen -Klassen explizit mit Hilfe des Schlüsselworts `import` importiert werden. +die Klassen eines Entwicklungspaketes verwenden zu können, müssen sie mit dem +Schlüsselwort `import` importiert werden. ```mermaid flowchart diff --git a/docs/documentation/data-objects.md b/docs/documentation/data-objects.md index 80da16846b..288b50a17d 100644 --- a/docs/documentation/data-objects.md +++ b/docs/documentation/data-objects.md @@ -135,8 +135,8 @@ public class MainClass { Der Cast-Operator `()` erlaubt die explizite Umwandlung eines Datentyps in einen anderen. Bei Wertzuweisungen findet außerdem eine implizite Typumwandlung vom -niederwertigen zum höherwertigen Datentyp statt. Zu beachten ist, dass bei einer -Typumwandlung ein Genauigkeitsverlust auftreten kann. +niederwertigen zum höherwertigen Datentyp statt. Bei einer Typumwandlung kann +ein Genauigkeitsverlust auftreten. ```java title="MainClass.java" showLineNumbers public class MainClass { diff --git a/docs/documentation/exceptions.mdx b/docs/documentation/exceptions.mdx index 8db8f535f1..7ab9b8af76 100644 --- a/docs/documentation/exceptions.mdx +++ b/docs/documentation/exceptions.mdx @@ -59,6 +59,9 @@ classDiagram RunTimeException <|-- ArithmeticException ``` +Eine Übersicht wichtiger Ausnahmenklassen findet sich in der +[Java API](java-api). + ## Definition von Ausnahmenklassen Eigene Ausnahmenklassen werden durch Ableitung von einer bestehenden @@ -133,3 +136,45 @@ public class MainClass { } ``` + +## Der finally-Block + +Der optionale `finally`-Block wird nach dem `try`- und allen `catch`-Blöcken +immer ausgeführt — unabhängig davon, ob eine Ausnahme aufgetreten ist oder +nicht. Er eignet sich daher für Aufräumarbeiten wie das Schließen von +Ressourcen. + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + try { + Notebook notebook = new Notebook("Mein Gaming Laptop", new Cpu(4.7, 8), 32, 16); + } catch (InvalidValueException e) { + System.err.println(e.getMessage()); + } finally { + System.out.println("wird immer ausgeführt"); + } + } + +} +``` + +Werden Ressourcen wie Dateiströme geöffnet, empfiehlt sich statt `finally` die +_Try-with-Resources_-Anweisung. Klassen, die `AutoCloseable` implementieren, +werden dabei am Ende des `try`-Blocks automatisch geschlossen — auch im +Fehlerfall. + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + try (FileReader reader = new FileReader("data.txt")) { + // Datei lesen + } catch (IOException e) { + System.err.println(e.getMessage()); + } + } + +} +``` diff --git a/docs/documentation/hashing.mdx b/docs/documentation/hashing.mdx index 37fa66276b..c78db3ec27 100644 --- a/docs/documentation/hashing.mdx +++ b/docs/documentation/hashing.mdx @@ -130,3 +130,49 @@ einer Suche wird zuerst der Bucket bestimmt, dann der Bucket durchsucht. + +## Hashing in Java + +Java verwendet Hashing intern in den Klassen [`HashMap`](maps) und +[`HashSet`](java-collections-framework). Der Schlüssel eines Eintrags +bestimmt über seinen Hashcode den Bucket, in dem der Eintrag abgelegt wird. Bei +einer Suche wird zunächst der Bucket über den Hashcode gefunden, anschließend +wird innerhalb des Buckets mit `equals()` verglichen. + +Damit diese Mechanismen korrekt funktionieren, müssen die Methoden `hashCode()` +und `equals()` der [Klasse `Object`](object) konsistent überschrieben werden: + +- Zwei inhaltlich gleiche Objekte (`equals()` gibt `true` zurück) müssen + **denselben Hashcode** liefern. +- Zwei Objekte mit demselben Hashcode müssen **nicht** inhaltlich gleich sein + (eine Kollision ist erlaubt, aber ineffizient). + +```java title="Person.java (Auszug)" showLineNumbers +public class Person { + + private String name; + private int age; + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + Person other = (Person) object; + return age == other.age && Objects.equals(name, other.name); + } + + @Override + public int hashCode() { + return Objects.hash(name, age); + } + +} +``` + +:::warning + +Wird `equals()` überschrieben, ohne `hashCode()` anzupassen, verhält sich die +Klasse in `HashMap` und `HashSet` falsch: Zwei inhaltlich gleiche Objekte landen +in unterschiedlichen Buckets und werden nicht als gleich erkannt. + +::: diff --git a/docs/documentation/interfaces.mdx b/docs/documentation/interfaces.mdx index d6950b328f..b4b09d08ed 100644 --- a/docs/documentation/interfaces.mdx +++ b/docs/documentation/interfaces.mdx @@ -147,7 +147,7 @@ public class Smartphone implements MobileDevice { @Override public int getScreenSizeInInches() { - return Math.sqrt(Math.pow(widthInInches) + Math.pow(heightInInches)); + return (int) Math.sqrt(Math.pow(widthInInches, 2) + Math.pow(heightInInches, 2)); } ... } @@ -178,3 +178,11 @@ public class MainClass { + +:::tip + +Eine Schnittstelle eignet sich, wenn mehrere, voneinander unabhängige Klassen +dasselbe Verhalten teilen sollen. Eine [abstrakte Klasse](abstract-and-final) +eignet sich, wenn gemeinsame Implementierungen geerbt werden sollen. + +::: diff --git a/docs/documentation/java-stream-api.md b/docs/documentation/java-stream-api.md index 67c36ee893..eb6dd3ce49 100644 --- a/docs/documentation/java-stream-api.md +++ b/docs/documentation/java-stream-api.md @@ -100,7 +100,7 @@ intermediäre Operationen sind Filtern, Abbilden und Sortieren. | Operation | Methode | Schnittstellen-Methode | | ------------- | ---------------------------------------------------------- | -------------------------------- | | Filtern | `Stream filter(predicate: Predicate)` | `boolean test(t: T)` | -| Abbilden | `Stream map(mapper: Function)` | `R apply(t: T)` | +| Abbilden | `Stream map(mapper: Function)` | `R apply(t: T)` | | Abbilden | `DoubleStream mapToDouble(mapper: ToDoubleFunction)` | `double applyAsDouble(value: T)` | | Abbilden | `IntStream mapToInt(mapper: ToIntFunction)` | `int applyAsInt(value: T)` | | Abbilden | `LongStream mapToLong(mapper: ToLongFunction)` | `long applyAsLong(value: T)` | diff --git a/docs/documentation/lambdas.md b/docs/documentation/lambdas.md index d2a66aee60..25ba9949db 100644 --- a/docs/documentation/lambdas.md +++ b/docs/documentation/lambdas.md @@ -8,6 +8,28 @@ tags: [inner-classes, lambdas] Lambda-Ausdrücke sind anonyme Funktionen, die einer Variablen zugewiesen oder direkt als Parameter übergeben werden können. +## Funktionale Schnittstellen + +Voraussetzung für einen Lambda-Ausdruck ist eine _funktionale Schnittstelle_ — +eine Schnittstelle mit genau einer abstrakten Methode. Die Annotation +`@FunctionalInterface` kennzeichnet eine Schnittstelle als funktional und lässt +den Compiler prüfen, dass sie tatsächlich nur eine abstrakte Methode enthält. + +Das Paket `java.util.function` enthält häufig benötigte funktionale +Schnittstellen: + +| Schnittstelle | Abstrakte Methode | Beschreibung | +| ------------------ | --------------------------- | -------------------------------------- | +| `Predicate` | `boolean test(t: T)` | Prüft eine Bedingung | +| `Function` | `R apply(t: T)` | Bildet einen Wert auf einen anderen ab | +| `Consumer` | `void accept(t: T)` | Verarbeitet einen Wert ohne Rückgabe | +| `Supplier` | `T get()` | Liefert einen Wert ohne Eingabe | +| `UnaryOperator` | `T apply(t: T)` | Bildet einen Wert auf denselben Typ ab | +| `Comparator` | `int compare(o1: T, o2: T)` | Vergleicht zwei Werte | + +Diese Schnittstellen werden unter anderem von der +[Java Stream API](java-stream-api) verwendet. + ## Implementierung von Lambda-Ausdrücken Die Parameterliste und der Methodenkörper eines Lambda-Ausdrucks werden durch @@ -29,13 +51,6 @@ public class MainClass { } ``` -:::info - -Voraussetzung für Lambda-Ausdrücke ist eine _funktionale Schnittstelle_ — eine -Schnittstelle mit genau einer abstrakten Methode. - -::: - ## Syntaxvarianten Die Syntax von Lambda-Ausdrücken erlaubt verschiedene Kurzformen: diff --git a/docs/documentation/object.md b/docs/documentation/object.md index 3eae6ce980..b0b7ae5811 100644 --- a/docs/documentation/object.md +++ b/docs/documentation/object.md @@ -65,6 +65,15 @@ eine einfache Möglichkeit zur Implementierung von `hashCode()`. ::: +:::warning + +`equals()` und `hashCode()` müssen immer gemeinsam überschrieben werden. Gilt +für zwei Objekte `a.equals(b) == true`, muss auch `a.hashCode() == b.hashCode()` +gelten. Andernfalls verhalten sich hash-basierte Datenstrukturen wie `HashMap` +und `HashSet` falsch (siehe [Schlüsseltransformationen](hashing)). + +::: + ## Die Methode _String toString()_ Die Methode `String toString()` liefert eine eindeutige Kennung des Objekts in diff --git a/docs/documentation/trees.md b/docs/documentation/trees.md index 9100396a69..1c410b2aec 100644 --- a/docs/documentation/trees.md +++ b/docs/documentation/trees.md @@ -117,3 +117,33 @@ flowchart TD depth[Tiefendurchlauf: 6, 2, 1, 4, 3, 5, 8, 7, 9 Breitendurchlauf: 6, 2, 8, 1, 4, 7, 9, 3, 5] ``` + +## Binäre Suchbäume in Java + +Java verwendet binäre Suchbäume intern in den Klassen +[`TreeSet`](java-collections-framework) und [`TreeMap`](maps). Ein +binärer Suchbaum hält seine Elemente stets sortiert: Jeder Knoten ist größer als +alle Knoten im linken Teilbaum und kleiner als alle Knoten im rechten Teilbaum. +Dadurch ist die Suche, das Einfügen und das Löschen in 𝒪(log 𝑛) möglich. + +Damit `TreeSet` und `TreeMap` Elemente vergleichen können, muss die gespeicherte +Klasse entweder `Comparable` implementieren oder beim Erzeugen der Sammlung +ein `Comparator` übergeben werden (siehe [Komparatoren](comparators)). + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + TreeSet numbers = new TreeSet<>(); + numbers.add(6); + numbers.add(2); + numbers.add(8); + numbers.add(1); + numbers.add(4); + + // Ausgabe erfolgt aufsteigend sortiert: 1 2 4 6 8 + numbers.forEach(System.out::println); + } + +} +``` From 17321ca52679b50c08356c7ec9ec6e4cb1b6d9d0 Mon Sep 17 00:00:00 2001 From: "daniel.appenmaier@gmail.com" Date: Thu, 25 Jun 2026 09:07:06 +0200 Subject: [PATCH 2/2] Improvements and Small Fixes by Claude --- docs/documentation/comparators.md | 73 ++++++++++++++++++++++ docs/documentation/enumerations.md | 88 ++++++++++++++++++++++++++- docs/documentation/generics.md | 6 +- docs/documentation/java-stream-api.md | 7 --- docs/documentation/optionals.md | 86 ++++++++++++++++++++++++++ docs/documentation/polymorphism.mdx | 2 +- docs/documentation/tests.md | 4 ++ 7 files changed, 254 insertions(+), 12 deletions(-) diff --git a/docs/documentation/comparators.md b/docs/documentation/comparators.md index 0a1faa4b79..012170ddec 100644 --- a/docs/documentation/comparators.md +++ b/docs/documentation/comparators.md @@ -62,3 +62,76 @@ public class MainClass { } ``` + +## Komparatoren als Lambda-Ausdrücke + +Da `Comparator` ein funktionales Interface ist, lässt sich ein Komparator +statt als eigene Klasse auch als Lambda-Ausdruck schreiben. Das spart +Boilerplate und macht den Sortierauftrag direkt sichtbar. + +```java title="MainClass.java (Auszug)" showLineNumbers +// Aufsteigend nach Prozessorleistung – als Lambda statt eigener Klasse +Collections.sort(notebooks, (o1, o2) -> Double.compare( + o1.cpu().powerInGhz(), o2.cpu().powerInGhz())); +``` + +## Komparatoren mit Comparator.comparing() + +Die statische Fabrikmethode +`Comparator comparing(keyExtractor: Function)` erstellt einen +Komparator anhand eines Schlüsselextraktors. Das Ergebnis ist kompakter als ein +manuell implementiertes Lambda und lässt sich direkt mit `List.sort()` +verwenden. + +```java title="MainClass.java (Auszug)" showLineNumbers +// Aufsteigend nach Beschreibung +notebooks.sort(Comparator.comparing(Notebook::description)); + +// Aufsteigend nach Arbeitsspeicher +notebooks.sort(Comparator.comparingInt(Notebook::memoryInGb)); +``` + +## Umkehren der Sortierung mit reversed() + +Die Instanzmethode `Comparator reversed()` kehrt die Sortierreihenfolge eines +bestehenden Komparators um, ohne dass ein neues Lambda geschrieben werden muss. + +```java title="MainClass.java (Auszug)" showLineNumbers +// Absteigend nach Arbeitsspeicher +notebooks.sort(Comparator.comparingInt(Notebook::memoryInGb).reversed()); +``` + +## Mehrstufige Sortierung mit thenComparing() + +Mit `Comparator thenComparing(other: Comparator)` lassen sich mehrere +Sortierkriterien verknüpfen. Das zweite Kriterium greift nur dann, wenn das +erste einen Gleichstand liefert. + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + List notebooks = new ArrayList<>(); + notebooks.add(new Notebook("Mein Office Laptop", new Cpu(3.5, 8), 16, 14)); + notebooks.add(new Notebook("Mein Gaming Laptop", new Cpu(4.7, 8), 32, 16)); + notebooks.add(new Notebook("Mein zweites Office Laptop", new Cpu(3.5, 4), 16, 13)); + + // Erst aufsteigend nach Arbeitsspeicher, bei Gleichstand nach Prozessorleistung absteigend + Comparator comparator = Comparator + .comparingInt(Notebook::memoryInGb) + .thenComparingDouble((Notebook n) -> n.cpu().powerInGhz()) + .reversed(); + + notebooks.sort(comparator); + } + +} +``` + +:::tip + +Bevorzuge `Comparator.comparing()` und `thenComparing()` gegenüber manuellen +Lambda-Ausdrücken — der Code bleibt lesbar und die Sortierabsicht ist auf einen +Blick erkennbar. + +::: diff --git a/docs/documentation/enumerations.md b/docs/documentation/enumerations.md index 49bf9d83ab..6cc44bcece 100644 --- a/docs/documentation/enumerations.md +++ b/docs/documentation/enumerations.md @@ -49,6 +49,8 @@ public enum Weekday { Aufzählungen stellen eine Reihe hilfreicher Methoden bereit: +- `String name()` — gibt den Namen der Konstante exakt so zurück, wie er im + Quellcode deklariert wurde (z.B. `"MONDAY"`) - `T[] values()` — statisch; gibt alle Aufzählungskonstanten als Feld zurück - `T valueOf(name: String)` — statisch; gibt die Aufzählungskonstante zur angegebenen Zeichenkette zurück @@ -59,10 +61,94 @@ public class MainClass { public static void main(String[] args) { Weekday monday = Weekday.valueOf("MONDAY"); + System.out.println(monday.name()); // MONDAY + System.out.println(monday.ordinal()); // 0 + for (Weekday w : Weekday.values()) { - System.out.println(w.ordinal()); + System.out.println(w.ordinal() + ": " + w.description()); } } } ``` + +:::info + +`name()` und `toString()` liefern bei einfachen Aufzählungen dasselbe Ergebnis. +`name()` ist jedoch fest spezifiziert und kann nicht überschrieben werden; +`toString()` kann dagegen in der Aufzählung überschrieben werden, um eine +angepasste Darstellung zu liefern. + +::: + +## Abstrakte Methoden in Aufzählungen + +Aufzählungen können abstrakte Methoden deklarieren, wenn jede Konstante ein +individuelles Verhalten bereitstellen soll. Jede Aufzählungskonstante muss die +abstrakte Methode dann mit einer eigenen Implementierung überschreiben. + +```java title="Operation.java" showLineNumbers +public enum Operation { + + ADD { + @Override + public double apply(double a, double b) { + return a + b; + } + }, + SUBTRACT { + @Override + public double apply(double a, double b) { + return a - b; + } + }, + MULTIPLY { + @Override + public double apply(double a, double b) { + return a * b; + } + }, + DIVIDE { + @Override + public double apply(double a, double b) { + if (b == 0) throw new ArithmeticException("Division durch null"); + return a / b; + } + }; + + public abstract double apply(double a, double b); + +} +``` + +In der Startklasse können alle Operationen einheitlich über den gemeinsamen +Methodenaufruf verwendet werden, ohne dass eine `switch`-Fallunterscheidung +nötig ist. + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + public static void main(String[] args) { + double a = 10; + double b = 3; + + for (Operation op : Operation.values()) { + System.out.printf("%s: %.2f%n", op.name(), op.apply(a, b)); + } + // ADD: 13,00 + // SUBTRACT: 7,00 + // MULTIPLY: 30,00 + // DIVIDE: 3,33 + } + +} +``` + +:::tip + +Abstrakte Methoden in Aufzählungen eignen sich besonders dann, wenn sich +Verhalten je nach Konstante unterscheidet und ein `switch`-Ausdruck über die +Aufzählung vermieden werden soll. Das Muster ist eine kompakte Variante des +_Strategy_-Entwurfsmusters. + +::: diff --git a/docs/documentation/generics.md b/docs/documentation/generics.md index 5a6fbab8e4..d4f523e8f4 100644 --- a/docs/documentation/generics.md +++ b/docs/documentation/generics.md @@ -196,19 +196,19 @@ public class MainClass { covariantBox = new Box(); // Kompilierungsfehler covariantBox = new Box(); covariantBox = new Box(); - covariantBox = new Box(); // Kompilierungsfehler + covariantBox = new Box(); // Kompilierungsfehler Box contravariantBox; contravariantBox = new Box(); contravariantBox = new Box(); contravariantBox = new Box(); // Kompilierungsfehler - covariantBox = new Box(); // Kompilierungsfehler + contravariantBox = new Box(); // Kompilierungsfehler Box invariantBox; invariantBox = new Box(); // Kompilierungsfehler invariantBox = new Box(); invariantBox = new Box(); // Kompilierungsfehler - covariantBox = new Box(); // Kompilierungsfehler + invariantBox = new Box(); // Kompilierungsfehler } } diff --git a/docs/documentation/java-stream-api.md b/docs/documentation/java-stream-api.md index eb6dd3ce49..c038993aa3 100644 --- a/docs/documentation/java-stream-api.md +++ b/docs/documentation/java-stream-api.md @@ -68,13 +68,6 @@ public class MainClass { } ``` -:::note - -Die Zahlenfolge 4-8-15-16-23-42 spielt eine große Rolle in der Fernsehserie -_Lost_. - -::: - Im Gegensatz zu `Stream` bieten die spezialisierten Klassen `IntStream`, `DoubleStream` und `LongStream` zusätzliche Methoden zur Verarbeitung primitiver Werte, wie etwa `sum()` oder `average()`. diff --git a/docs/documentation/optionals.md b/docs/documentation/optionals.md index 0fc42c520f..1a31947314 100644 --- a/docs/documentation/optionals.md +++ b/docs/documentation/optionals.md @@ -67,3 +67,89 @@ public class MainClass { } ``` + +## Transformieren mit map() und flatMap() + +Neben dem direkten Zugriff erlaubt `Optional` das Transformieren des enthaltenen +Werts, ohne den `null`-Fall explizit behandeln zu müssen. Die Methode +`Optional map(mapper: Function)` wendet eine Funktion auf den Wert an +und verpackt das Ergebnis wieder in ein `Optional`. Ist der Ausgangswert nicht +vorhanden, liefert `map()` ein leeres `Optional` zurück. + +```java title="MainClass.java" showLineNumbers +public class MainClass { + + private static List names; + + public static void main(String[] args) { + names = List.of("Hans", "Anna", "Klaus"); + + // Liefert die Länge des Namens oder nichts, falls kein Name gefunden + Optional nameLength = getNameByInitial('A') + .map(String::length); + nameLength.ifPresent(System.out::println); // 4 + + // Liefert den Namen in Großbuchstaben oder einen Standardwert + String upper = getNameByInitial('Z') + .map(String::toUpperCase) + .orElse("unbekannt"); + System.out.println(upper); // unbekannt + } + + public static Optional getNameByInitial(char initial) { + return names.stream() + .filter(n -> n.charAt(0) == initial) + .findFirst(); + } + +} +``` + +Die Methode `Optional flatMap(mapper: Function>)` wird +eingesetzt, wenn die Transformationsfunktion selbst ein `Optional` zurückgibt. +Sie verhindert dabei das Entstehen verschachtelter +`Optional>`-Werte. + +```java title="MainClass.java (Auszug)" showLineNumbers +// map() würde Optional> liefern – flatMap() vermeidet das +Optional result = Optional.of(" hallo ") + .flatMap(s -> s.isBlank() ? Optional.empty() : Optional.of(s.trim())); +System.out.println(result); // Optional[hallo] +``` + +## Filtern mit filter() + +Mit `Optional filter(predicate: Predicate)` lässt sich der enthaltene Wert +anhand einer Bedingung prüfen. Erfüllt der Wert das Prädikat, bleibt das +`Optional` unverändert; anderenfalls wird ein leeres `Optional` zurückgegeben. + +```java title="MainClass.java (Auszug)" showLineNumbers +Optional longName = getNameByInitial('H') + .filter(n -> n.length() > 3); +longName.ifPresent(System.out::println); // Hans (Länge 4 > 3) + +Optional shortName = getNameByInitial('H') + .filter(n -> n.length() > 10); +System.out.println(shortName.isPresent()); // false +``` + +## Fehlerbehandlung mit orElseThrow() + +Soll das Fehlen eines Werts als Fehlerfall gewertet werden, bietet +`T orElseThrow(exceptionSupplier: Supplier)` eine präzise Alternative zu +`orElse()`. Ist kein Wert vorhanden, wird die angegebene Ausnahme ausgelöst. + +```java title="MainClass.java (Auszug)" showLineNumbers +// Wirft eine IllegalArgumentException, wenn kein Name mit 'X' existiert +String name = getNameByInitial('X') + .orElseThrow(() -> new IllegalArgumentException("Kein Name gefunden")); +``` + +:::warning + +`Optional.get()` sollte nur aufgerufen werden, wenn vorher `isPresent()` geprüft +wurde. Ohne diese Prüfung kann `get()` eine `NoSuchElementException` auslösen — +genau wie der ursprüngliche `null`-Zugriff. Bevorzuge stattdessen `orElse()`, +`orElseThrow()` oder `ifPresent()`. + +::: diff --git a/docs/documentation/polymorphism.mdx b/docs/documentation/polymorphism.mdx index 500fd2a7f6..7f1b896a1a 100644 --- a/docs/documentation/polymorphism.mdx +++ b/docs/documentation/polymorphism.mdx @@ -42,7 +42,7 @@ public class Computer { public class Notebook extends Computer { ... public Notebook(String description, Cpu cpu, int memoryInGb, double screenSizeInInches) { - super(description, cpu, mainMemoryInGb); + super(description, cpu, memoryInGb); this.screenSizeInInches = screenSizeInInches; } diff --git a/docs/documentation/tests.md b/docs/documentation/tests.md index 59f95eceaa..b709139457 100644 --- a/docs/documentation/tests.md +++ b/docs/documentation/tests.md @@ -36,3 +36,7 @@ stattfindet, wird die testgetriebene Entwicklung zu den Designstrategien gezählt. ::: + +Die praktische Umsetzung von Komponententests mit JUnit 5 — Annotationen, +Assertions und Beispiele — ist auf der Seite +[Komponententests (Unit Tests)](unit-tests) beschrieben.