Ciao Paolo, anche se l'incontro non è disponibile puoi reperire la registrazione dell'evento al JUG Milano qui
http://www.jugmilano.it/meeting-98.html http://www.jugmilano.it/meeting-98.html Come promesso pubblico anche una possibile soluzione all'esercizio. Ringrazio ancora Matteo Mortari che ha ingegnosamente risolto l'esercizio in Java, la soluzione è allegata e se non dovesse essere chiara proverò a delucidarvi. Ho approfittato inoltre per cogliere il suggerimento di Simone riscrivendo la soluzione, togliendo quasi tutti gli operatori reattivi ed utilizzando preferibilmente le coroutine. Il secondo esercizio è: fun countActive(): Flux<Sensor> = GlobalScope.flux { controlUnit.getSensors().consumeEach { sensor -> launch { val value = iotClient.query(sensor).awaitFirst() if (value.isFinite()) send(sensor) } "flux", come detto, è un generatore di flussi reattivi, per far emettere un sensore al flusso bisogna utilizzare il metodo "send". "consumeEach" serve per iterare sullo stream reattivo. "launch" fa partire una coroutine, è praticamente l'analogo di "new Thread". "awaitFirst" sospende la coroutine aspettando il primo valore emesso dal sensore. La soluzione al terzo esercizio è: fun checkSensor() = GlobalScope.flux { val configurationDeferred = async { database.loadConfiguration().collectList().awaitFirst() } controlUnit.getSensors().consumeEach { sensor -> launch { val average = iotClient.query(sensor) .take(3) .collectList().awaitFirst() .filter { it.isFinite() } .takeIf { it.isNotEmpty() } ?.average() val configuration = checkNotNull(configurationDeferred.await().find { it.sensorType == sensor.type && it.context == sensor.context }) if (average != null && average !in configuration.validRange) { send(AbstractMap.SimpleEntry(sensor, average)) // sent to Flux } } } } "async" lancia una coroutine e ne cattura il risultato in un "Deferred" (una sorta di CompletableFuture). "takeIf" è il famoso operatore magico, ritorna l'offetto dse la condizione è vera altrimenti torna "null" "?.average()" calcola la media, il simbolo "?." invece del semplice "." è il safe operator, cioè calcola la media della lista solo se la lista non è null, altrimenti ritorna direttamente null. in Kotlin non è possibile chiamare un metodo su un oggetto null, è un errore di compilazione (in Java è un errore a runtime). "await" sul configurationDeferred attande il risultato del deferred, tipo "future.get()" "find" cerca un elemento nella lista, oppure ritorna null "checkNotNull" verifica che il parametro non sia null e lo ritorna, altrimenti lancia un errore "average !in validRange" è compilato esattamente come il codice Java "!validRange.contains(average)" Se approfondite le soluzioni proposte nei due linguaggi risulta più evidente che gli stream reattivi hanno molti operatori solo per sopperire a deficienze del linguaggio, nel codice Kotlin si riesce a fare quasi tutto con async/await. Similmente è possibile estendere il discorso per il CompletableFuture e gli Optional. Grazie per la bella serata di ieri, arrivederci alla prossima. Vasco
