Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions docs/documentation/comparators.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,76 @@ public class MainClass {

}
```

## Komparatoren als Lambda-Ausdrücke

Da `Comparator<T>` 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<T> comparing(keyExtractor: Function<T, U>)` 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<T> 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<T> thenComparing(other: Comparator<T>)` 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<Notebook> 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<Notebook> 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.

:::
88 changes: 87 additions & 1 deletion docs/documentation/enumerations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

:::
6 changes: 3 additions & 3 deletions docs/documentation/generics.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,19 +196,19 @@ public class MainClass {
covariantBox = new Box<Object>(); // Kompilierungsfehler
covariantBox = new Box<Number>();
covariantBox = new Box<Integer>();
covariantBox = new Box<Integer>(); // Kompilierungsfehler
covariantBox = new Box<String>(); // Kompilierungsfehler

Box<? super Number> contravariantBox;
contravariantBox = new Box<Object>();
contravariantBox = new Box<Number>();
contravariantBox = new Box<Integer>(); // Kompilierungsfehler
covariantBox = new Box<Integer>(); // Kompilierungsfehler
contravariantBox = new Box<String>(); // Kompilierungsfehler

Box<Number> invariantBox;
invariantBox = new Box<Object>(); // Kompilierungsfehler
invariantBox = new Box<Number>();
invariantBox = new Box<Integer>(); // Kompilierungsfehler
covariantBox = new Box<String>(); // Kompilierungsfehler
invariantBox = new Box<String>(); // Kompilierungsfehler
}

}
Expand Down
7 changes: 0 additions & 7 deletions docs/documentation/java-stream-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` bieten die spezialisierten Klassen `IntStream`,
`DoubleStream` und `LongStream` zusätzliche Methoden zur Verarbeitung primitiver
Werte, wie etwa `sum()` oder `average()`.
Expand Down
86 changes: 86 additions & 0 deletions docs/documentation/optionals.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<U> map(mapper: Function<T, U>)` 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<String> 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<Integer> 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<String> getNameByInitial(char initial) {
return names.stream()
.filter(n -> n.charAt(0) == initial)
.findFirst();
}

}
```

Die Methode `Optional<U> flatMap(mapper: Function<T, Optional<U>>)` wird
eingesetzt, wenn die Transformationsfunktion selbst ein `Optional` zurückgibt.
Sie verhindert dabei das Entstehen verschachtelter
`Optional<Optional<T>>`-Werte.

```java title="MainClass.java (Auszug)" showLineNumbers
// map() würde Optional<Optional<String>> liefern – flatMap() vermeidet das
Optional<String> 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<T> filter(predicate: Predicate<T>)` 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<String> longName = getNameByInitial('H')
.filter(n -> n.length() > 3);
longName.ifPresent(System.out::println); // Hans (Länge 4 > 3)

Optional<String> 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<X>)` 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()`.

:::
2 changes: 1 addition & 1 deletion docs/documentation/polymorphism.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 4 additions & 0 deletions docs/documentation/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Loading