@@ -17,7 +17,8 @@ tldr: |
1717 Man kann Optionals prüfen mit `isEmpty()` und `ifPresent()` und dann direkt mit
1818 `ifPresent()`, `orElse()` und `orElseThrow()` auf den verpackten Wert zugreifen.
1919 Besser ist aber der Zugriff über die Stream-API von `Optional`: `map()`, `filter`,
20- `flatMap()`, ...
20+ `flatMap()`, ... Dabei gibt es keine terminalen Operationen - es handelt sich ja auch
21+ nicht um einen Stream, nur die Optik erinnert daran.
2122
2223
2324 `Optional` ist vor allem für Rückgabewerte gedacht, die den Fall "kein Wert vorhanden"
@@ -50,6 +51,105 @@ attachments:
5051 - link : " https://github.com/Programmiermethoden-CampusMinden/Prog2-Lecture/blob/_pdf/lecture/java-modern/optional.pdf"
5152 name : " PDF-Version"
5253challenges : |
54+ **Optional und Stream-API**
55+
56+ 1. Erklären Sie den folgenden Code-Schnipsel aus dem [Dungeon](https://github.com/Dungeon-CampusMinden/Dungeon/pull/1831):
57+
58+ ```java
59+ Skill fireball =
60+ new Skill(
61+ new FireballSkill(
62+ () ->
63+ hero.fetch(CollideComponent.class)
64+ .map(cc -> cc.center(hero).add(viewDirection.toPoint()))
65+ .orElseThrow(
66+ () -> MissingComponentException.build(hero, CollideComponent.class)),
67+ FIREBALL_RANGE,
68+ FIREBALL_SPEED,
69+ FIREBALL_DMG),
70+ 1);
71+ ```
72+
73+ Hinweis: `Entity#fetch`: `<T extends Component> Optional<T> fetch(final Class<T> klass)`.
74+
75+ <!--
76+ `fetch` liefert ein `Optional` zurück. `map` packt das aus und wendet die Funktion an und verpackt das
77+ Ergebnis erneut in ein `Optional` - wenn das ursprüngliche Optional leer war oder das Ergebnis des
78+ Funktionsaufrufs `null` ist, dann bleibt/ergibt sich ein leeres Optional. Das `orElseThrow` packt das
79+ `Optional` aus und liefert das verpackte Objekt zurück oder wirft eine Exception, wenn nichts verpackt
80+ war.
81+ -->
82+
83+ 2. Was würde sich ändern, wenn statt `map` ein `flatMap` verwendet würde? Wie ist das bei richtigen
84+ Streams?
85+
86+ <!--
87+ `Optional#map`: `<U> Optional<U> map(Function<? super T, ? extends U> mapper)`. Wendet eine Funktion
88+ mit `T -> U` und verpackt das Funktionsergebnis in ein `Optional`, Ergebnis `Optional<U>`.
89+
90+ `Optional#flatMap`: `<U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)`.
91+ Wendet eine Funktion mit `T -> Optional<U>` an, welches das Ergebnis der Operation ist.
92+
93+ `flatMap` würde das Funktionsergebnis nicht selbst noch einmal in ein `Optional` verpacken - das muss
94+ die Funktion diesmal selbst tun.
95+
96+ Bei Streams würde `flatMap` die durch die Funktionsaufrufe pro Element erhaltenen Streams auspacken und
97+ diese Elemente in den Ergebnis-Stream packen.
98+ -->
99+
100+ 3. Was passiert im folgenden Beispiel? Warum funktioniert das auch ohne terminale Stream-Operation?
101+
102+ ```java
103+ Game.hero()
104+ .flatMap(e -> e.fetch(AmmunitionComponent.class))
105+ .map(AmmunitionComponent::resetCurrentAmmunition);
106+ ```
107+
108+ Hinweis: `Game.hero()`: `static Optional<Entity> hero()`.
109+
110+ <!--
111+ Wir haben hier keine Streams, sondern ein `Optional`. Da gibt es keine terminale Stream-Operation, das
112+ `flatMap` und `map` sind wie verschachtelte Abfragen auf `null` mit folgender Operation.
113+ -->
114+
115+ 4. Können Sie die beiden obigen Beispiele in "klassischer" Schreibweise umformulieren?
116+
117+ <!--
118+ ```java
119+ // "modern"
120+ () ->
121+ hero.fetch(CollideComponent.class)
122+ .map(cc -> cc.center(hero).add(viewDirection.toPoint()))
123+ .orElseThrow(
124+ () -> MissingComponentException.build(hero, CollideComponent.class))
125+
126+ // "classic"
127+ Optional<CollideComponent> cc = hero.fetch(CollideComponent.class);
128+ if (cc.isPresent()) {
129+ cc.get().center(hero).add(viewDirection.toPoint());
130+ } else {
131+ throw MissingComponentException.build(hero, CollideComponent.class);
132+ }
133+ ```
134+
135+ ```java
136+ // "modern"
137+ Game.hero()
138+ .flatMap(e -> e.fetch(AmmunitionComponent.class))
139+ .map(AmmunitionComponent::resetCurrentAmmunition);
140+
141+ // "classic"
142+ Optional<Entity> hero = Game.hero();
143+ if (hero.isPresent()) {
144+ Optional<AmmunitionComponent> ac = hero.get().fetch(AmmunitionComponent.class);
145+ if (ac.isPresent()) {
146+ ac.get().resetCurrentAmmunition();
147+ }
148+ }
149+ ```
150+ -->
151+
152+
53153 **String-Handling**
54154
55155 Können Sie den folgenden Code so umschreiben, dass Sie statt der `if`-Abfragen und der einzelnen direkten Methodenaufrufe
0 commit comments