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
Questo è l'ultimo codelab del bootcamp Kotlin. In questo codelab imparerai a utilizzare le annotazioni e le interruzioni etichettate. Esaminerai le espressioni lambda e le funzioni di ordine superiore, che sono parti fondamentali di Kotlin. Scopri di più anche sulle funzioni inline e sulle interfacce Single Abstract Method (SAM). Infine, scopri di più sulla libreria standard Kotlin.
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 creare una nuova classe in IntelliJ IDEA ed eseguire un programma
- Nozioni di base su espressioni lambda e funzioni di ordine superiore
Obiettivi didattici
- Nozioni di base sulle annotazioni
- Come utilizzare le pause etichettate
- Scopri di più sulle funzioni di ordine superiore
- Informazioni sulle interfacce Single Abstract Method (SAM)
- Informazioni sulla libreria standard di Kotlin
In questo lab proverai a:
- Crea un'annotazione semplice.
- Utilizza un'interruzione etichettata.
- Esamina le funzioni lambda in Kotlin.
- Utilizzare e creare funzioni di ordine superiore.
- Chiama alcune interfacce di metodi astratti singoli.
- Utilizzare alcune funzioni della libreria standard Kotlin.
Le annotazioni sono un modo per allegare metadati al codice e non sono specifiche di Kotlin. Le annotazioni vengono lette dal compilatore e utilizzate per generare codice o logica. Molti framework, come Ktor e Kotlinx, nonché Room, utilizzano le annotazioni per configurare il modo in cui vengono eseguiti e interagiscono con il codice. È improbabile che tu incontri annotazioni prima di iniziare a utilizzare i framework, ma è utile sapere come leggerne una.
Esistono anche annotazioni disponibili tramite la libreria standard Kotlin che controllano il modo in cui viene compilato il codice. Sono molto utili se esporti codice Kotlin in Java, ma altrimenti non ti servono spesso.
Le annotazioni vengono inserite immediatamente prima dell'elemento annotato e la maggior parte degli elementi può essere annotata: classi, funzioni, metodi e persino strutture di controllo. Alcune annotazioni possono accettare argomenti.
Ecco un esempio di alcune annotazioni.
@file:JvmName("InteropFish")
class InteropFish {
companion object {
@JvmStatic fun interop()
}
}Questo indica che il nome esportato di questo file è InteropFish con l'annotazione JvmName; l'annotazione JvmName accetta l'argomento "InteropFish". Nell'oggetto complementare, @JvmStatic indica a Kotlin di rendere interop() una funzione statica in InteropFish.
Puoi anche creare annotazioni personalizzate, ma questa operazione è utile soprattutto se stai scrivendo una libreria che ha bisogno di informazioni particolari sulle classi in fase di runtime, ovvero la reflection.
Passaggio 1: crea un nuovo pacchetto e un nuovo file
- In src, crea un nuovo pacchetto,
example. - Nell'esempio, crea un nuovo file Kotlin,
Annotations.kt.
Passaggio 2: crea la tua annotazione
- In
Annotations.kt, crea una classePlantcon due metodi,trim()efertilize().
class Plant {
fun trim(){}
fun fertilize(){}
}- Crea una funzione che stampa tutti i metodi di una classe. Utilizza
::classper ottenere informazioni su una classe in fase di runtime. UtilizzadeclaredMemberFunctionsper ottenere un elenco dei metodi di una classe. Per accedere a questa funzionalità, devi importarekotlin.reflect.full.*.
import kotlin.reflect.full.* // required import
class Plant {
fun trim(){}
fun fertilize(){}
}
fun testAnnotations() {
val classObj = Plant::class
for (m in classObj.declaredMemberFunctions) {
println(m.name)
}
}- Crea una funzione
main()per chiamare la routine di test. Esegui il programma e osserva l'output.
fun main() {
testAnnotations()
}⇒ trim fertilize
- Crea un'annotazione semplice,
ImAPlant.
annotation class ImAPlantQuesta opzione non fa altro che indicare che è annotata.
- Aggiungi l'annotazione davanti alla classe
Plant.
@ImAPlant class Plant{
...
}- Modifica
testAnnotations()per stampare tutte le annotazioni di un corso. Utilizzaannotationsper visualizzare tutte le annotazioni di un corso. Esegui il programma e osserva il risultato.
fun testAnnotations() {
val plantObject = Plant::class
for (a in plantObject.annotations) {
println(a.annotationClass.simpleName)
}
}⇒ ImAPlant
- Modifica
testAnnotations()per trovare l'annotazioneImAPlant. UtilizzafindAnnotation()per trovare un'annotazione specifica. Esegui il programma e osserva il risultato.
fun testAnnotations() {
val plantObject = Plant::class
val myAnnotationObject = plantObject.findAnnotation<ImAPlant>()
println(myAnnotationObject)
}
⇒ @example.ImAPlant()
Passaggio 3: crea un'annotazione mirata
Le annotazioni possono avere come target getter o setter. In questo caso, puoi applicarli con il prefisso @get: o @set:. Questo problema si verifica spesso quando si utilizzano framework con annotazioni.
- Dichiara due annotazioni,
OnGet, che può essere applicata solo ai getter delle proprietà, eOnSet, che può essere applicata solo ai setter delle proprietà. Utilizza@Target(AnnotationTarger.PROPERTY_GETTER)oPROPERTY_SETTERsu ciascuno.
annotation class ImAPlant
@Target(AnnotationTarget.PROPERTY_GETTER)
annotation class OnGet
@Target(AnnotationTarget.PROPERTY_SETTER)
annotation class OnSet
@ImAPlant class Plant {
@get:OnGet
val isGrowing: Boolean = true
@set:OnSet
var needsFood: Boolean = false
}Le annotazioni sono molto utili per creare librerie che ispezionano gli elementi sia in fase di runtime sia, a volte, in fase di compilazione. Tuttavia, il codice dell'applicazione tipico utilizza solo le annotazioni fornite dai framework.
Kotlin offre diversi modi per controllare il flusso. Hai già familiarità con return, che restituisce il controllo da una funzione alla funzione che la contiene. L'utilizzo di un break è simile a return, ma per i loop.
Kotlin ti offre un controllo aggiuntivo sui loop con quello che viene chiamato interruzione con etichetta. Un break qualificato con un'etichetta passa al punto di esecuzione subito dopo il ciclo contrassegnato con quell'etichetta. Ciò è particolarmente utile quando si ha a che fare con loop nidificati.
Qualsiasi espressione in Kotlin può essere contrassegnata con un'etichetta. Le etichette hanno la forma di un identificatore seguito dal simbolo @.
- In
Annotations.kt, prova un'interruzione etichettata uscendo da un ciclo interno.
fun labels() {
outerLoop@ for (i in 1..100) {
print("$i ")
for (j in 1..100) {
if (i > 10) break@outerLoop // breaks to outer loop
}
}
}
fun main() {
labels()
}- Esegui il programma e osserva l'output.
⇒ 1 2 3 4 5 6 7 8 9 10 11
Allo stesso modo, puoi utilizzare un continue etichettato. Anziché uscire dal ciclo etichettato, l'istruzione continue etichettata passa all'iterazione successiva del ciclo.
Le funzioni lambda sono funzioni anonime, ovvero funzioni senza nome. Puoi assegnarli a variabili e passarli come argomenti a funzioni e metodi. Sono estremamente utili.
Passaggio 1: crea una semplice funzione Lambda
- Avvia REPL in IntelliJ IDEA, Strumenti > Kotlin > Kotlin REPL.
- Crea una funzione lambda con un argomento,
dirty: Int, che esegue un calcolo dividendodirtyper 2. Assegna la lambda a una variabile,waterFilter.
val waterFilter = { dirty: Int -> dirty / 2 }- Chiama
waterFilter, passando un valore di 30.
waterFilter(30)⇒ res0: kotlin.Int = 15
Passaggio 2: crea una lambda di filtro
- Sempre in REPL, crea una classe di dati,
Fish, con una proprietà,name.
data class Fish(val name: String)- Crea un elenco di tre
Fishcon i nomi Flipper, Moby Dick e Dory.
val myFish = listOf(Fish("Flipper"), Fish("Moby Dick"), Fish("Dory"))- Aggiungi un filtro per verificare i nomi che contengono la lettera "i".
myFish.filter { it.name.contains("i")}
⇒ res3: kotlin.collections.List<Line_1.Fish> = [Fish(name=Flipper), Fish(name=Moby Dick)]
Nell'espressione lambda, it si riferisce all'elemento corrente dell'elenco e il filtro viene applicato a ogni elemento dell'elenco a turno.
- Applica
joinString()al risultato, utilizzando", "come separatore.
myFish.filter { it.name.contains("i")}.joinToString(", ") { it.name }
⇒ res4: kotlin.String = Flipper, Moby Dick
La funzione joinToString() crea una stringa unendo i nomi filtrati, separati dalla stringa specificata. È una delle tante funzioni utili integrate nella libreria standard Kotlin.
Il passaggio di una funzione lambda o di un'altra funzione come argomento a una funzione crea una funzione di ordine superiore. Il filtro riportato sopra è un semplice esempio. filter() è una funzione a cui viene passata una funzione lambda che specifica come elaborare ogni elemento dell'elenco.
Scrivere funzioni di ordine superiore con espressioni lambda di estensione è una delle parti più avanzate del linguaggio Kotlin. Ci vuole un po' di tempo per imparare a scriverli, ma sono davvero comodi da usare.
Passaggio 1: crea un nuovo corso
- All'interno del pacchetto example, crea un nuovo file Kotlin,
Fish.kt. - In
Fish.kt, crea una classe di datiFishcon una proprietà,name.
data class Fish (var name: String)- Crea una funzione
fishExamples(). InfishExamples(), crea un pesce denominato"splashy", tutto in minuscolo.
fun fishExamples() {
val fish = Fish("splashy") // all lowercase
}- Crea una funzione
main()che chiamafishExamples().
fun main () {
fishExamples()
}- Compila ed esegui il programma facendo clic sul triangolo verde a sinistra di
main(). Ancora nessun output.
Passaggio 2: utilizza una funzione di ordine superiore
La funzione with() ti consente di creare uno o più riferimenti a un oggetto o a una proprietà in modo più compatto. In uso: this. with() è in realtà una funzione di ordine superiore e nella lambda specifichi cosa fare con l'oggetto fornito.
- Utilizza
with()per scrivere il nome del pesce in maiuscolo infishExamples(). Tra le parentesi graffe,thissi riferisce all'oggetto passato awith().
fun fishExamples() {
val fish = Fish("splashy") // all lowercase
with (fish.name) {
this.capitalize()
}
}- Non è presente alcun output, quindi aggiungi un
println()intorno. Inoltre,thisè implicito e non necessario, quindi puoi rimuoverlo.
fun fishExamples() {
val fish = Fish("splashy") // all lowercase
with (fish.name) {
println(capitalize())
}
}⇒ Splashy
Passaggio 3: crea una funzione di ordine superiore
Dietro le quinte, with() è una funzione di ordine superiore. Per vedere come funziona, puoi creare una tua versione molto semplificata di with() che funziona solo per le stringhe.
- In
Fish.kt, definisci una funzione,myWith(), che accetta due argomenti. Gli argomenti sono l'oggetto su cui operare e una funzione che definisce l'operazione. La convenzione per il nome dell'argomento con la funzione èblock. In questo caso, la funzione non restituisce nulla, come specificato conUnit.
fun myWith(name: String, block: String.() -> Unit) {}All'interno di myWith(), block() ora è una funzione di estensione di String. La classe estesa viene spesso chiamata oggetto ricevitore. Quindi, name è l'oggetto ricevente in questo caso.
- Nel corpo di
myWith(), applica la funzione passata,block(), all'oggetto ricevitore,name.
fun myWith(name: String, block: String.() -> Unit) {
name.block()
}- In
fishExamples(), sostituisciwith()conmyWith().
fun fishExamples() {
val fish = Fish("splashy") // all lowercase
myWith (fish.name) {
println(capitalize())
}
}fish.name è l'argomento name e println(capitalize()) è la funzione block.
- Esegui il programma e funzionerà come prima.
⇒ Splashy
Passaggio 4: esplora altre estensioni integrate
L'estensione lambda with() è molto utile e fa parte della libreria standard Kotlin. Ecco alcuni degli altri che potresti trovare utili: run(), apply() e let().
La funzione run() è un'estensione che funziona con tutti i tipi. Prende una funzione lambda come argomento e restituisce il risultato dell'esecuzione della funzione lambda.
- In
fishExamples(), chiamarun()al numerofishper ottenere il nome.
fish.run {
name
}Restituisce solo la proprietà name. Puoi assegnarlo a una variabile o stamparlo. Questo non è un esempio utile, in quanto potresti semplicemente accedere alla proprietà, ma run() può essere utile per espressioni più complicate.
La funzione apply() è simile a run(), ma restituisce l'oggetto modificato a cui è stata applicata anziché il risultato della lambda. Può essere utile per chiamare metodi su un oggetto appena creato.
- Crea una copia di
fishe chiamaapply()per impostare il nome della nuova copia.
val fish2 = Fish(name = "splashy").apply {
name = "sharky"
}
println(fish2.name)
⇒ sharky
La funzione let() è simile a apply(), ma restituisce una copia dell'oggetto con le modifiche. Ciò può essere utile per concatenare le manipolazioni.
- Utilizza
let()per ottenere il nome difish, mettilo in maiuscolo, concatenalo a un'altra stringa, ottieni la lunghezza del risultato, aggiungi 31 alla lunghezza e stampa il risultato.
println(fish.let { it.name.capitalize()}
.let{it + "fish"}
.let{it.length}
.let{it + 31})⇒ 42
In questo esempio, il tipo di oggetto a cui fa riferimento it è Fish, poi String, poi di nuovo String e infine Int.
- Stampa
fishdopo aver chiamatolet()e vedrai che non è cambiato.
println(fish.let { it.name.capitalize()}
.let{it + "fish"}
.let{it.length}
.let{it + 31})
println(fish)⇒ 42 Fish(name=splashy)
Le espressioni lambda e le funzioni di ordine superiore sono molto utili, ma c'è una cosa che devi sapere: le espressioni lambda sono oggetti. Un'espressione lambda è un'istanza di un'interfaccia Function, che a sua volta è un sottotipo di Object. Considera l'esempio precedente di myWith().
myWith(fish.name) {
capitalize()
}L'interfaccia Function ha un metodo, invoke(), che viene sostituito per chiamare l'espressione lambda. Scritto per esteso, avrebbe un aspetto simile al codice riportato di seguito.
// actually creates an object that looks like this
myWith(fish.name, object : Function1<String, Unit> {
override fun invoke(name: String) {
name.capitalize()
}
})Normalmente questo non è un problema, perché la creazione di oggetti e la chiamata di funzioni non comportano un overhead elevato, ovvero un utilizzo eccessivo di memoria e tempo della CPU. Tuttavia, se definisci qualcosa come myWith() che utilizzi ovunque, il sovraccarico potrebbe aumentare.
Kotlin fornisce inline come modo per gestire questo caso per ridurre l'overhead durante il runtime aggiungendo un po' di lavoro in più per il compilatore. (Hai imparato qualcosa su inline nella lezione precedente sui tipi concreti). Se contrassegni una funzione come inline, ogni volta che viene chiamata, il compilatore trasformerà il codice sorgente per incorporare la funzione. ovvero il compilatore modificherà il codice per sostituire l'espressione lambda con le istruzioni al suo interno.
Se myWith() nell'esempio precedente è contrassegnato con inline:
inline myWith(fish.name) {
capitalize()
}viene trasformato in una chiamata diretta:
// with myWith() inline, this becomes
fish.name.capitalize()È importante notare che l'incorporamento di funzioni di grandi dimensioni aumenta le dimensioni del codice, quindi è meglio utilizzarlo per funzioni semplici che vengono utilizzate molte volte, come myWith(). Le funzioni di estensione delle librerie che hai imparato a conoscere in precedenza sono contrassegnate con inline, quindi non devi preoccuparti della creazione di oggetti aggiuntivi.
Single Abstract Method indica semplicemente un'interfaccia con un solo metodo. Sono molto comuni quando si utilizzano API scritte nel linguaggio di programmazione Java, per cui esiste un acronimo: SAM. Alcuni esempi sono Runnable, che ha un unico metodo astratto, run(), e Callable, che ha un unico metodo astratto, call().
In Kotlin, devi chiamare sempre le funzioni che accettano SAM come parametri. Prova l'esempio riportato di seguito.
- All'interno di example, crea una classe Java,
JavaRun, e incolla quanto segue nel file.
package example;
public class JavaRun {
public static void runNow(Runnable runnable) {
runnable.run();
}
}Kotlin consente di creare un'istanza di un oggetto che implementa un'interfaccia precedendo il tipo con object:. È utile per passare parametri ai SAM.
- In
Fish.kt, crea una funzionerunExample(), che crea unRunnableutilizzandoobject:. L'oggetto deve implementarerun()stampando"I'm a Runnable".
fun runExample() {
val runnable = object: Runnable {
override fun run() {
println("I'm a Runnable")
}
}
}- Chiama
JavaRun.runNow()con l'oggetto che hai creato.
fun runExample() {
val runnable = object: Runnable {
override fun run() {
println("I'm a Runnable")
}
}
JavaRun.runNow(runnable)
}- Chiama
runExample()damain()ed esegui il programma.
⇒ I'm a Runnable
Molto lavoro per stampare qualcosa, ma un buon esempio di come funziona un SAM. Naturalmente, Kotlin offre un modo più semplice per farlo: utilizza una lambda al posto dell'oggetto per rendere questo codice molto più compatto.
- Rimuovi il codice esistente in
runExample, modificalo in modo che chiamirunNow()con una lambda ed esegui il programma.
fun runExample() {
JavaRun.runNow({
println("Passing a lambda as a Runnable")
})
}
⇒ Passing a lambda as a Runnable
- Puoi rendere questa formula ancora più concisa utilizzando la sintassi dell'ultima chiamata di parametri ed eliminando le parentesi.
fun runExample() {
JavaRun.runNow {
println("Last parameter is a lambda as a Runnable")
}
}⇒ Last parameter is a lambda as a Runnable
Queste sono le basi di un SAM, un Single Abstract Method. Puoi creare un'istanza, eseguire l'override e chiamare un SAM con una sola riga di codice, utilizzando il pattern:Class.singleAbstractMethod { lambda_of_override }
Questa lezione ha esaminato le espressioni lambda e ha approfondito le funzioni di ordine superiore, parti fondamentali di Kotlin. Hai anche imparato a utilizzare le annotazioni e le interruzioni etichettate.
- Utilizza le annotazioni per specificare elementi al compilatore. Ad esempio:
@file:JvmName("Foo") - Utilizza le interruzioni etichettate per consentire al codice di uscire dall'interno dei loop nidificati. Ad esempio:
if (i > 10) break@outerLoop // breaks to outerLoop label - Le espressioni lambda possono essere molto potenti se combinate con funzioni di ordine superiore.
- Le espressioni lambda sono oggetti. Per evitare di creare l'oggetto, puoi contrassegnare la funzione con
inlinee il compilatore inserirà direttamente i contenuti della lambda nel codice. - Utilizza
inlinecon attenzione, ma può contribuire a ridurre l'utilizzo delle risorse da parte del programma. - SAM (Single Abstract Method) è un pattern comune e semplificato con le espressioni lambda. Il pattern di base è:
Class.singleAbstractMethod { lamba_of_override } - La libreria standard Kotlin fornisce numerose funzioni utili, tra cui diverse SAM, quindi scopri cosa contiene.
Kotlin offre molto di più di quanto trattato nel corso, ma ora hai le basi per iniziare a sviluppare i tuoi programmi Kotlin. Ci auguriamo che questo linguaggio espressivo ti entusiasmi e che non vedi l'ora di creare più funzionalità scrivendo meno codice (soprattutto se provieni dal linguaggio di programmazione Java). La pratica e l'apprendimento sul campo sono il modo migliore per diventare esperti di Kotlin, quindi continua a esplorare e imparare Kotlin in autonomia.
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.
- Annotazioni
- Riflessione
- Interruzioni etichettate
- Funzioni di ordine superiore e lambdas
- Funzioni in linea
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.
Libreria standard Kotlin
La libreria standard Kotlin fornisce numerose funzioni utili. Prima di scrivere una funzione o un'interfaccia, controlla sempre la libreria standard per vedere se qualcuno ti ha risparmiato del lavoro. Controlla di tanto in tanto, perché vengono aggiunte spesso nuove funzionalità.
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
In Kotlin, SAM sta per:
▢ Safe Argument Matching
▢ Metodo di accesso semplice
▢ Metodo astratto singolo
▢ Metodologia di accesso strategico
Domanda 2
Quale delle seguenti non è una funzione di estensione della libreria standard Kotlin?
▢ elvis()
▢ apply()
▢ run()
▢ with()
Domanda 3
Quale delle seguenti affermazioni non è vera per le espressioni lambda in Kotlin?
▢ Le funzioni lambda sono funzioni anonime.
▢ Le espressioni lambda sono oggetti, a meno che non siano inlined.
▢ Le espressioni lambda richiedono molte risorse e non devono essere utilizzate.
▢ Le espressioni lambda possono essere trasmesse ad altre funzioni.
Domanda 4
Le etichette in Kotlin sono indicate con un identificatore seguito da:
▢ :
▢ ::
▢ @:
▢ @
Complimenti! Hai completato il codelab Kotlin Bootcamp for Programmers.
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).