Questo codelab fa parte del corso Kotlin Bootcamp for Programmers. Per ottenere il massimo valore da questo corso, ti consigliamo di seguire le codelab in sequenza. A seconda delle tue conoscenze, potresti riuscire a leggere rapidamente alcune sezioni. Questo corso è rivolto ai programmatori che conoscono un linguaggio orientato agli oggetti e vogliono imparare Kotlin.
Introduzione
In questo codelab vengono presentate diverse funzionalità utili di Kotlin, tra cui coppie, raccolte e funzioni di estensione.
Anziché creare una singola app di esempio, le lezioni di questo corso sono progettate per sviluppare le tue conoscenze, ma sono semi-indipendenti l'una dall'altra, in modo che tu possa scorrere rapidamente le sezioni che conosci. Per collegarli, molti esempi utilizzano un tema acquatico. Se vuoi scoprire tutta la storia dell'acquario, dai un'occhiata al corso Kotlin Bootcamp for Programmers di Udacity.
Cosa devi già sapere
- La sintassi di funzioni, classi e metodi Kotlin
- Come utilizzare il ciclo REPL (Read-Eval-Print Loop) di Kotlin in IntelliJ IDEA
- Come creare una nuova classe in IntelliJ IDEA ed eseguire un programma
Obiettivi didattici
- Come lavorare con coppie e triple
- Scopri di più sulle raccolte
- Definizione e utilizzo delle costanti
- Scrivere funzioni di estensione
In questo lab proverai a:
- Scopri di più su coppie, triple e hash map nel REPL
- Scopri diversi modi per organizzare le costanti
- Scrivere una funzione di estensione e una proprietà di estensione
In questa attività imparerai a conoscere le coppie e le triple e a destrutturarle. Le coppie e le terne sono classi di dati predefinite per 2 o 3 elementi generici. Ad esempio, può essere utile per fare in modo che una funzione restituisca più di un valore.
Supponiamo di avere un List
di pesci e una funzione isFreshWater()
per verificare se il pesce è d'acqua dolce o salata. List.partition()
restituisce due elenchi: uno con gli elementi in cui la condizione è true
e l'altro con gli elementi in cui la condizione è false
.
val twoLists = fish.partition { isFreshWater(it) }
println("freshwater: ${twoLists.first}")
println("saltwater: ${twoLists.second}")
Passaggio 1: crea alcune coppie e triple
- Apri REPL (Strumenti > Kotlin > Kotlin REPL).
- Crea una coppia, associando un'attrezzatura all'uso a cui è destinata, quindi stampa i valori. Puoi creare una coppia creando un'espressione che collega due valori, ad esempio due stringhe, con la parola chiave
to
, quindi utilizzando.first
o.second
per fare riferimento a ciascun valore.
val equipment = "fish net" to "catching fish"
println("${equipment.first} used for ${equipment.second}")
⇒ fish net used for catching fish
- Crea una tripla e stampala con
toString()
, poi convertila in un elenco contoList()
. Crea una tripla utilizzandoTriple()
con 3 valori. Utilizza.first
,.second
e.third
per fare riferimento a ciascun valore.
val numbers = Triple(6, 9, 42)
println(numbers.toString())
println(numbers.toList())
⇒ (6, 9, 42) [6, 9, 42]
Gli esempi precedenti utilizzano lo stesso tipo per tutte le parti della coppia o del triplo, ma non è obbligatorio. Le parti possono essere una stringa, un numero o un elenco, ad esempio, o anche un'altra coppia o tripla.
- Crea una coppia in cui la prima parte della coppia è a sua volta una coppia.
val equipment2 = ("fish net" to "catching fish") to "equipment"
println("${equipment2.first} is ${equipment2.second}\n")
println("${equipment2.first.second}")
⇒ (fish net, catching fish) is equipment ⇒ catching fish
Passaggio 2: destruttura alcune coppie e triple
La separazione di coppie e triple nei loro componenti è chiamata destrutturazione. Assegna la coppia o la tripla al numero appropriato di variabili e Kotlin assegnerà il valore di ogni parte in ordine.
- Destruttura una coppia e stampa i valori.
val equipment = "fish net" to "catching fish"
val (tool, use) = equipment
println("$tool is used for $use")
⇒ fish net is used for catching fish
- Destruttura una tripla e stampa i valori.
val numbers = Triple(6, 9, 42)
val (n1, n2, n3) = numbers
println("$n1 $n2 $n3")
⇒ 6 9 42
Tieni presente che la destrutturazione di coppie e triple funziona allo stesso modo delle classi di dati, argomento trattato in un codelab precedente.
In questa attività scoprirai di più sulle raccolte, tra cui gli elenchi, e su un nuovo tipo di raccolta, le hash map.
Passaggio 1: scopri di più sugli elenchi
- Gli elenchi e gli elenchi modificabili sono stati introdotti in una lezione precedente. Sono una struttura di dati molto utile, quindi Kotlin fornisce una serie di funzioni integrate per le liste. Consulta questo elenco parziale di funzioni per gli elenchi. Puoi trovare elenchi completi nella documentazione di Kotlin per
List
eMutableList
.
Funzione | Purpose |
| Aggiungi un elemento all'elenco modificabile. |
| Rimuovere un elemento da un elenco modificabile. |
| Restituisce una copia dell'elenco con gli elementi in ordine inverso. |
| Restituisce |
| Restituisce una parte dell'elenco, dal primo indice fino al secondo indice escluso. |
- Mentre lavori ancora in REPL, crea un elenco di numeri e chiama
sum()
. che riassume tutti gli elementi.
val list = listOf(1, 5, 3, 4)
println(list.sum())
⇒ 13
- Crea un elenco di stringhe e sommale.
val list2 = listOf("a", "bbb", "cc")
println(list2.sum())
⇒ error: none of the following functions can be called with the arguments supplied:
- Se l'elemento non è qualcosa che
List
sa sommare direttamente, ad esempio una stringa, puoi specificare come sommarlo utilizzando.sumBy()
con una funzione lambda, ad esempio per sommare in base alla lunghezza di ogni stringa. Il nome predefinito di un argomento lambda èit
eit
si riferisce a ogni elemento dell'elenco durante l'attraversamento.
val list2 = listOf("a", "bbb", "cc")
println(list2.sumBy { it.length })
⇒ 6
- Puoi fare molto di più con gli elenchi. Un modo per visualizzare la funzionalità disponibile è creare un elenco in IntelliJ IDEA, aggiungere il punto e poi esaminare l'elenco del completamento automatico nel suggerimento. Questa operazione funziona per qualsiasi oggetto. Prova con un elenco.
- Scegli
listIterator()
dall'elenco, quindi scorri l'elenco con un'istruzionefor
e stampa tutti gli elementi separati da spazi.
val list2 = listOf("a", "bbb", "cc")
for (s in list2.listIterator()) {
println("$s ")
}
⇒ a bbb cc
Passaggio 2: prova le mappe hash
In Kotlin, puoi mappare praticamente qualsiasi cosa a qualsiasi altra cosa utilizzando hashMapOf()
. Le mappe hash sono una sorta di elenco di coppie, in cui il primo valore funge da chiave.
- Crea una mappa hash che corrisponda a sintomi, chiavi e malattie dei pesci, i valori.
val cures = hashMapOf("white spots" to "Ich", "red sores" to "hole disease")
- Puoi quindi recuperare il valore della malattia in base alla chiave del sintomo utilizzando
get()
o, in modo ancora più breve, le parentesi quadre[]
.
println(cures.get("white spots"))
⇒ Ich
println(cures["red sores"])
⇒ hole disease
- Prova a specificare un sintomo che non è presente nella mappa.
println(cures["scale loss"])
⇒ null
Se una chiave non è nella mappa, il tentativo di restituire la malattia corrispondente restituisce null
. A seconda dei dati della mappa, è normale che non ci sia corrispondenza per una possibile chiave. Per casi come questo, Kotlin fornisce la funzione getOrDefault()
.
- Prova a cercare una chiave che non ha corrispondenze utilizzando
getOrDefault()
.
println(cures.getOrDefault("bloating", "sorry, I don't know"))
⇒ sorry, I don't know
Se devi fare di più che restituire un valore, Kotlin fornisce la funzione getOrElse()
.
- Modifica il codice in modo che utilizzi
getOrElse()
anzichégetOrDefault()
.
println(cures.getOrElse("bloating") {"No cure for this"})
⇒ No cure for this
Anziché restituire un semplice valore predefinito, viene eseguito il codice compreso tra le parentesi graffe {}
. Nell'esempio, else
restituisce semplicemente una stringa, ma potrebbe essere sofisticato come trovare una pagina web con una cura e restituirla.
Come per mutableListOf
, puoi anche creare un mutableMapOf
. Una mappa modificabile ti consente di inserire e rimuovere elementi. Modificabile significa che può essere modificato, immutabile significa che non può essere modificato.
- Crea una mappa dell'inventario modificabile, mappando una stringa di attrezzature al numero di articoli. Crealo con una rete da pesca, poi aggiungi 3 spugne per acquari all'inventario con
put()
e rimuovi la rete da pesca conremove()
.
val inventory = mutableMapOf("fish net" to 1)
inventory.put("tank scrubber", 3)
println(inventory.toString())
inventory.remove("fish net")
println(inventory.toString())
⇒ {fish net=1, tank scrubber=3}{tank scrubber=3}
In questa attività, scoprirai le costanti in Kotlin e i diversi modi per organizzarle.
Passaggio 1: scopri le differenze tra const e val
- Nel REPL, prova a creare una costante numerica. In Kotlin, puoi creare costanti di primo livello e assegnare loro un valore in fase di compilazione utilizzando
const val
.
const val rocks = 3
Il valore viene assegnato e non può essere modificato, il che assomiglia molto alla dichiarazione di una normale val
. Qual è la differenza tra const val
e val
? Il valore di const val
viene determinato in fase di compilazione, mentre il valore di val
viene determinato durante l'esecuzione del programma, il che significa che val
può essere assegnato da una funzione in fase di runtime.
Ciò significa che a val
può essere assegnato un valore da una funzione, ma a const val
no.
val value1 = complexFunctionCall() // OK
const val CONSTANT1 = complexFunctionCall() // NOT ok
Inoltre, const val
funziona solo a livello principale e nelle classi singleton dichiarate con object
, non con le classi normali. Puoi utilizzarlo per creare un file o un oggetto singleton che contenga solo costanti e importarle in base alle necessità.
object Constants {
const val CONSTANT2 = "object constant"
}
val foo = Constants.CONSTANT2
Passaggio 2: crea un oggetto companion
Kotlin non ha il concetto di costanti a livello di classe.
Per definire le costanti all'interno di una classe, devi racchiuderle in oggetti complementari dichiarati con la parola chiave companion
. L'oggetto companion è fondamentalmente un oggetto singleton all'interno della classe.
- Crea una classe con un oggetto complementare contenente una costante stringa.
class MyClass {
companion object {
const val CONSTANT3 = "constant in companion"
}
}
La differenza fondamentale tra gli oggetti companion e gli oggetti normali è:
- Gli oggetti companion vengono inizializzati dal costruttore statico della classe contenitore, ovvero vengono creati quando viene creato l'oggetto.
- Gli oggetti regolari vengono inizializzati in modo differito al primo accesso all'oggetto, ovvero quando vengono utilizzati per la prima volta.
C'è altro, ma per ora ti basterà sapere che devi racchiudere le costanti nelle classi in un oggetto companion.
In questa attività imparerai a estendere il comportamento delle classi. È molto comune scrivere funzioni di utilità per estendere il comportamento di una classe. Kotlin fornisce una sintassi comoda per dichiarare queste funzioni di utilità: le funzioni di estensione.
Le funzioni di estensione ti consentono di aggiungere funzioni a una classe esistente senza dover accedere al relativo codice sorgente. Ad esempio, puoi dichiararli in un file Extensions.kt che fa parte del tuo pacchetto. In realtà, questa operazione non modifica la classe, ma ti consente di utilizzare la notazione con il punto quando chiami la funzione sugli oggetti di quella classe.
Passaggio 1: scrivi una funzione di estensione
- Mentre lavori ancora nella REPL, scrivi una semplice funzione di estensione,
hasSpaces()
, per verificare se una stringa contiene spazi. Il nome della funzione è preceduto dalla classe su cui opera. All'interno della funzione,this
si riferisce all'oggetto su cui viene chiamata, mentreit
si riferisce all'iteratore nella chiamatafind()
.
fun String.hasSpaces(): Boolean {
val found = this.find { it == ' ' }
return found != null
}
println("Does it have spaces?".hasSpaces())
⇒ true
- Puoi semplificare la funzione
hasSpaces()
.this
non è necessario in modo esplicito e la funzione può essere ridotta a una singola espressione e restituita, quindi non sono necessarie nemmeno le parentesi graffe{}
.
fun String.hasSpaces() = find { it == ' ' } != null
Passaggio 2: scopri le limitazioni delle estensioni
Le funzioni di estensione hanno accesso solo all'API pubblica della classe che estendono. Le variabili private
non sono accessibili.
- Prova ad aggiungere funzioni di estensione a una proprietà contrassegnata con
private
.
class AquariumPlant(val color: String, private val size: Int)
fun AquariumPlant.isRed() = color == "red" // OK
fun AquariumPlant.isBig() = size > 50 // gives error
⇒ error: cannot access 'size': it is private in 'AquariumPlant'
- Esamina il codice riportato di seguito e scopri cosa verrà stampato.
open class AquariumPlant(val color: String, private val size: Int)
class GreenLeafyPlant(size: Int) : AquariumPlant("green", size)
fun AquariumPlant.print() = println("AquariumPlant")
fun GreenLeafyPlant.print() = println("GreenLeafyPlant")
val plant = GreenLeafyPlant(size = 10)
plant.print()
println("\n")
val aquariumPlant: AquariumPlant = plant
aquariumPlant.print() // what will it print?
⇒ GreenLeafyPlant AquariumPlant
plant.print()
stampe GreenLeafyPlant
. Potresti aspettarti che anche aquariumPlant.print()
stampi GreenLeafyPlant
, perché gli è stato assegnato il valore plant
. Tuttavia, il tipo viene risolto in fase di compilazione, quindi viene stampato AquariumPlant
.
Passaggio 3: aggiungi una proprietà di estensione
Oltre alle funzioni di estensione, Kotlin ti consente anche di aggiungere proprietà di estensione. Come per le funzioni di estensione, devi specificare la classe che stai estendendo, seguita da un punto e dal nome della proprietà.
- Mentre lavori ancora in REPL, aggiungi una proprietà di estensione
isGreen
aAquariumPlant
, che ètrue
se il colore è verde.
val AquariumPlant.isGreen: Boolean
get() = color == "green"
È possibile accedere alla proprietà isGreen
come a una proprietà normale. Quando si accede, viene chiamato il getter per isGreen
per ottenere il valore.
- Stampa la proprietà
isGreen
per la variabileaquariumPlant
e osserva il risultato.
aquariumPlant.isGreen
⇒ res4: kotlin.Boolean = true
Passaggio 4: informazioni sui destinatari nullabili
La classe che estendi è chiamata ricevitore ed è possibile renderla nullable. Se lo fai, la variabile this
utilizzata nel corpo può essere null
, quindi assicurati di eseguire il test. Ti consigliamo di utilizzare un ricevitore nullable se prevedi che i chiamanti vogliano chiamare il tuo metodo di estensione su variabili nullable o se vuoi fornire un comportamento predefinito quando la funzione viene applicata a null
.
- Continuando a lavorare in REPL, definisci un metodo
pull()
che accetta un ricevitore nullable. Ciò è indicato con un punto interrogativo?
dopo il tipo, prima del punto. All'interno del corpo, puoi verificare sethis
non ènull
utilizzando il punto interrogativo-punto-applica?.apply.
fun AquariumPlant?.pull() {
this?.apply {
println("removing $this")
}
}
val plant: AquariumPlant? = null
plant.pull()
- In questo caso, non viene generato alcun output quando esegui il programma. Poiché
plant
ènull
, la funzioneprintln()
interna non viene chiamata.
Le funzioni di estensione sono molto potenti e la maggior parte della libreria standard Kotlin è implementata come funzioni di estensione.
In questa lezione hai scoperto di più sulle raccolte, sulle costanti e hai avuto un assaggio della potenza delle funzioni e delle proprietà di estensione.
- Le coppie e le triple possono essere utilizzate per restituire più di un valore da una funzione. Ad esempio:
val twoLists = fish.partition { isFreshWater(it) }
- Kotlin ha molte funzioni utili per
List
, comereversed()
,contains()
esubList()
. - Un
HashMap
può essere utilizzato per mappare le chiavi ai valori. Ad esempio:val cures = hashMapOf("white spots" to "Ich", "red sores" to "hole disease")
- Dichiara le costanti in fase di compilazione utilizzando la parola chiave
const
. Puoi inserirli nel livello superiore, organizzarli in un oggetto singleton o inserirli in un oggetto complementare. - Un oggetto companion è un oggetto singleton all'interno di una definizione di classe, definito con la parola chiave
companion
. - Le funzioni e le proprietà delle estensioni possono aggiungere funzionalità a una classe. Ad esempio:
fun String.hasSpaces() = find { it == ' ' } != null
- Un ricevitore nullable ti consente di creare estensioni su una classe che può essere
null
. L'operatore?.
può essere abbinato aapply
per verificare la presenza dinull
prima di eseguire il codice. Ad esempio:this?.apply { println("removing $this") }
Documentazione di Kotlin
Se vuoi maggiori informazioni su un argomento di questo corso o se hai difficoltà, https://kotlinlang.org è il punto di partenza migliore.
Tutorial di Kotlin
Il sito web https://try.kotlinlang.org include tutorial dettagliati chiamati Kotlin Koans, un interprete basato sul web e una serie completa di documentazione di riferimento con esempi.
Corso Udacity
Per visualizzare il corso Udacity su questo argomento, consulta Kotlin Bootcamp for Programmers.
IntelliJ IDEA
La documentazione di IntelliJ IDEA è disponibile sul sito web di JetBrains.
Questa sezione elenca i possibili compiti a casa per gli studenti che seguono questo codelab nell'ambito di un corso guidato da un insegnante. Spetta all'insegnante:
- Assegna i compiti, se richiesto.
- Comunica agli studenti come inviare i compiti.
- Valuta i compiti a casa.
Gli insegnanti possono utilizzare questi suggerimenti nella misura che ritengono opportuna e sono liberi di assegnare qualsiasi altro compito a casa che ritengono appropriato.
Se stai seguendo questo codelab in autonomia, sentiti libero di utilizzare questi compiti per casa per mettere alla prova le tue conoscenze.
Rispondi a queste domande
Domanda 1
Quale delle seguenti funzioni restituisce una copia di un elenco?
▢ add()
▢ remove()
▢ reversed()
▢ contains()
Domanda 2
Quale di queste funzioni di estensione su class AquariumPlant(val color: String, val size: Int, private val cost: Double, val leafy: Boolean)
genererà un errore del compilatore?
▢ fun AquariumPlant.isRed() = color == "red"
▢ fun AquariumPlant.isBig() = size > 45
▢ fun AquariumPlant.isExpensive() = cost > 10.00
▢ fun AquariumPlant.isNotLeafy() = leafy == false
Domanda 3
Quale dei seguenti non è un luogo in cui puoi definire costanti con const val
?
▢ al livello principale di un file
▢ nelle classi regolari
▢ negli oggetti singleton
▢ in oggetti associati
Passa alla lezione successiva:
Per una panoramica del corso, inclusi i link ad altri codelab, vedi "Kotlin Bootcamp for Programmers: Welcome to the course" (Kotlin Bootcamp per programmatori: benvenuto al corso).