Skip to content

Commit 3bdb85f

Browse files
committed
lecture: improve the comparison of streams and optional (Optional)
add a remark to tldr. also add a new challenge ... closes #932
1 parent 5bd8608 commit 3bdb85f

File tree

1 file changed

+101
-1
lines changed

1 file changed

+101
-1
lines changed

lecture/java-modern/optional.md

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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"
5253
challenges: |
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

Comments
 (0)