In Java, espressioni lambda e
Stream API hanno costituito la principale innovazione della versione 8. L'uso di questi strumenti
in Java risulta piuttosto comodo in quanto è sufficiente invocare il metodo stream()
su una struttura dati per ottenere
uno Stream che permette di accedere in maniera dinamica al suo contenuto. In Kotlin, l'accesso a tali metodi è ancora più automatico, in quanto
i metodi tipici dell'interazione con gli Stream sono direttamente innestati nelle strutture dati, già incontrate in una
precedente lezione. Supponiamo di creare una data class che faccia da fondamento ad una categoria di oggetti contenenti due dati al proprio
interno: un Float che indica l'importo di una merce acquistata, ed una stringa che ne rappresenta la descrizione.
data class Importo(val descrizione:String, val importo: Float)
Creiamo una struttura dati lista contenente una raccolta di oggetti Importo e la inizializziamo con alcuni valori:
val importi= listOf<Importo>(
Importo("2 quaderni", 3.5f),
Importo("4 raccoglitori", 16f),
Importo("50 penne", 90f),
Importo("1 agenda", 12f),
Importo("10 matite", 8f),
Importo("3 righelli", 8.5f)
)
Come si è visto nel corso della guida, si può ottenere l'esecuzione di un determinato blocco di codice (in questo caso la stampa in output) su ognuno dei suoi elementi
utilizzando il metodo forEach
. Notiamo due aspetti che torneranno utili costantemente in questa lezione: forEach
non è
seguito da una coppia di parentesi tonde bensì da una di graffe; all'interno delle parentesi si può utilizzare il riferimento it
per indicare l'oggetto attualmente utilizzato dall'iterazione del ciclo.
importi.forEach { println(it) }
Ma con i metodi forniti si possono gestire molte altre operazioni di analisi dati. Ad esempio, possiamo applicare un filtro
con filter
, che in questo caso specifica che siamo interessati solo agli importi superiori a 20 euro:
val filtro=importi.filter { it.importo<20 }
// con il metodo count() possiamo conteggiare il numero di elementi filtrati
println(filtro.count())
Tra l'altro, esiste una versione di count
che permette di inserire al suo interno un filtro per effettuare in un
colpo solo le due operazioni precedenti:
val quantiFiltrati=importi.count { it.importo<20 }
Con l'operazione di map
possiamo richiedere di attuare una trasformazione che prelevi in input gli elementi della
struttura dati e restituisca in output il risultato di un'elaborazione a nostro piacimento. Ad esempio, nello snippet seguente
vogliamo che vengano letti in ingresso tutti gli oggetti della lista ma ne venga estratto solo l'importo: ciò al fine di avere una
lista di numeri su cui operare. La riga seguente calcola il totale di tutti gli importi presenti nella struttura dati:
val totale=importi.map { it.importo }.sum()
L'operazione di map
può essere eseguita a valle di quella di filter
vista in precedenza
per ottenere, ad esempio, la somma di tutti gli importi superiori a 20 euro presenti nella lista:
val totaleSopra20Euro=importi.filter{it.importo>20}.map { it.importo }.sum()
Queste non sono che alcune delle operazioni utilizzabili ma si consideri che tutto ciò che si può eseguire con le Stream API di Java
sarà in qualche modo applicabile in questi casi.