Kotlin From Scratch più divertimento con le funzioni

Kotlin è un linguaggio di programmazione moderno che viene compilato in bytecode Java. È gratuito e open source e promette di rendere la codifica per Android ancora più divertente.  

Nell'articolo precedente, hai imparato a conoscere i pacchetti e le funzioni di base in Kotlin. Le funzioni sono al centro di Kotlin, quindi in questo post le esamineremo più da vicino. Esploreremo i seguenti tipi di funzioni in Kotlin:

  • funzioni di primo livello
  • espressioni lambda o funzioni letterali
  • funzioni anonime
  • funzioni locali o annidate
  • funzioni infix
  • funzioni membro

Sarai stupito di tutte le cose interessanti che puoi fare con le funzioni di Kotlin!

1. Funzioni di primo livello

Le funzioni di livello superiore sono funzioni all'interno di un pacchetto Kotlin che sono definite al di fuori di qualsiasi classe, oggetto o interfaccia. Ciò significa che sono funzioni chiamate direttamente, senza la necessità di creare alcun oggetto o chiamare alcuna classe. 

Se sei un programmatore Java, sai che in genere creiamo metodi statici di utilità all'interno delle classi helper. Queste classi helper in realtà non fanno nulla, non hanno alcun metodo di stato o di istanza e agiscono semplicemente come contenitori per i metodi statici. Un tipico esempio è il collezioni classe nel java.util pacchetto e i suoi metodi statici. 

Le funzioni di primo livello in Kotlin possono essere utilizzate come alternativa ai metodi di utilità statica all'interno delle classi di helper che codifichiamo in Java. Diamo un'occhiata a come definire una funzione di primo livello in Kotlin. 

pacchetto com.chikekotlin.projectx.utils fun checkUserStatus (): String return "online"

Nel codice sopra, abbiamo definito un pacchetto com.chikekotlin.projectx.utils all'interno di un file chiamato UserUtils.kt e ha anche definito una funzione di utilità di primo livello chiamata checkUserStatus () all'interno di questo stesso pacchetto e file. Per brevità, questa funzione molto semplice restituisce la stringa "online". 

La prossima cosa che faremo è usare questa funzione di utilità in un altro pacchetto o file.

pacchetto com.chikekotlin.projectx.users import com.chikekotlin.projectx.utils.checkUserStatus if (checkUserStatus () == "online") // fai qualcosa

Nel codice precedente, abbiamo importato la funzione in un altro pacchetto e poi l'abbiamo eseguita! Come puoi vedere, non dobbiamo creare un oggetto o fare riferimento a una classe per chiamare questa funzione.

Interoperabilità Java

Dato che Java non supporta le funzioni di primo livello, il compilatore di Kotlin dietro le quinte creerà una classe Java e le singole funzioni di primo livello verranno convertite in metodi statici. Nel nostro caso, la classe Java generata era UserUtilsKt con un metodo statico checkUserStatus ()

/ * Java * / pacchetto com.chikekotlin.projectx.utils public class UserUtilsKt public static String checkUserStatus () return "online"; 

Ciò significa che i chiamanti Java possono semplicemente chiamare il metodo facendo riferimento alla sua classe generata, proprio come per qualsiasi altro metodo statico.

/ * Java * / import com.chikekotlin.projectx.utils.UserUtilsKt ... UserUtilsKt.checkUserStatus ()

Si noti che è possibile modificare il nome della classe Java che genera il compilatore Kotlin utilizzando @JvmName annotazione.

@file: JvmName ("UserUtils") pacchetto com.chikekotlin.projectx.utils fun checkUserStatus (): String return "online"

Nel codice sopra, abbiamo applicato il @JvmName annotazione e specificato un nome di classe UserUtilsper il file generato. Si noti inoltre che questa annotazione è posizionata all'inizio del file Kotlin, prima della definizione del pacchetto. 

Può essere referenziato da Java in questo modo:

/ * Java * / import com.chikekotlin.projectx.utils.UserUtils ... UserUtils.checkUserStatus ()

2. Espressioni Lambda

Le espressioni Lambda (o letterali di funzione) non sono inoltre vincolate a nessuna entità come una classe, un oggetto o un'interfaccia. Possono essere passati come argomenti ad altre funzioni chiamate funzioni di ordine superiore (ne discuteremo di più nel prossimo post). Un'espressione lambda rappresenta solo il blocco di una funzione e il loro utilizzo riduce il rumore nel nostro codice. 

Se sei un programmatore Java, sai che Java 8 e versioni successive forniscono il supporto per le espressioni lambda. Per usare espressioni lambda in un progetto che supporta versioni precedenti di Java come Java 7, 6 o 5, possiamo usare la famosa libreria Retrolambda. 

Una delle cose meravigliose di Kotlin è che le espressioni lambda sono supportate immediatamente. Poiché lambda non è supportato in Java 6 o 7, per far interagire Kotlin con esso, Kotlin crea una classe anonima Java dietro la scena. Nota che la creazione di un'espressione lambda in Kotlin è molto diversa da quella di Java.

Ecco le caratteristiche di un'espressione lambda in Kotlin:

  • Deve essere circondato da parentesi graffe .
  • Non ha il divertimento parola chiave. 
  • Non esiste un modificatore di accesso (privato, pubblico o protetto) perché non appartiene a nessuna classe, oggetto o interfaccia.
  • Non ha nome di funzione. In altre parole, è anonimo. 
  • Non viene specificato alcun tipo di ritorno perché sarà dedotto dal compilatore.
  • I parametri non sono circondati da parentesi ()

Inoltre, possiamo assegnare un'espressione lambda a una variabile e quindi eseguirla. 

Creazione di espressioni Lambda

Vediamo ora alcuni esempi di espressioni lambda. Nel codice qui sotto, abbiamo creato un'espressione lambda senza parametri e assegnata una variabile Messaggio. Abbiamo quindi eseguito l'espressione lambda chiamando Messaggio()

val message = println ("Ehi, Kotlin è davvero fantastico!") message () // "Ehi, Kotlin è davvero fantastico!"

Vediamo anche come includere i parametri in un'espressione lambda. 

val message = myString: String -> println (myString) message ("I love Kotlin") // Messaggio "I love Kotlin" ("Quanto lontano?") // "Quanto lontano?"

Nel codice sopra, abbiamo creato un'espressione lambda con il parametro mystring, insieme al tipo di parametro Stringa. Come puoi vedere, davanti al tipo di parametro, c'è una freccia: si riferisce al corpo lambda. In altre parole, questa freccia separa l'elenco dei parametri dal corpo lambda. Per renderlo più conciso, possiamo ignorare completamente il tipo di parametro (già dedotto dal compilatore). 

val message = myString -> println (myString) // verrà comunque compilato

Per avere più parametri, li separiamo semplicemente con una virgola. E ricorda, non avvolgere l'elenco dei parametri tra parentesi come in Java. 

val addNumbers = number1: Int, number2: Int -> println ("Adding $ number1 and $ number2") val result = number1 + number2 println ("Il risultato è $ result") addNumbers (1, 3)

Tuttavia, si noti che se i tipi di parametri non possono essere dedotti, devono essere specificati esplicitamente (come in questo esempio), altrimenti il ​​codice non verrà compilato.

Aggiunta di 1 e 3 Il risultato è 4

Passando Lambdas alle funzioni

Possiamo passare espressioni lambda come parametri alle funzioni: queste sono chiamate "funzioni di ordine superiore", perché sono funzioni di funzioni. Questo tipo di funzioni può accettare come parametro un lambda o una funzione anonima: ad esempio, il scorso() funzione di raccolta. 

Nel codice qui sotto, abbiamo passato un'espressione lambda al scorso() funzione. (Se vuoi un aggiornamento sulle raccolte in Kotlin, visita il terzo tutorial di questa serie) Come dice il nome, restituisce l'ultimo elemento nell'elenco.  scorso() accetta un'espressione lambda come parametro e questa espressione a sua volta prende un argomento di tipo Stringa. Il suo corpo funzione funge da predicato per cercare all'interno di un sottoinsieme di elementi nella raccolta. Ciò significa che l'espressione lambda deciderà quali elementi della collezione saranno considerati quando si cerca l'ultimo.

val stringList: List = listOf ("in", "the", "club") print (stringList.last ()) // stampa print "club" (stringList.last (s: String -> s.length == 3) ) // stamperà "il"

Vediamo come rendere l'ultima riga di codice sopra più leggibile.

stringList.last s: String -> s.length == 3 // inoltre compila e stampa "il" 

Il compilatore Kotlin ci consente di rimuovere le parentesi di funzione se l'ultimo argomento nella funzione è un'espressione lambda. Come è possibile osservare nel codice sopra, ci è stato permesso di farlo perché l'ultimo ed unico argomento passato al scorso() la funzione è un'espressione lambda. 

Inoltre, possiamo renderlo più conciso rimuovendo il tipo di parametro.

stringList.last s -> s.length == 3 // compilerà anche la stampa "the" 

Non è necessario specificare il tipo di parametro esplicitamente, poiché il tipo di parametro è sempre lo stesso del tipo di elemento di raccolta. Nel codice sopra, stiamo chiamando scorso su una raccolta di elenchi di Stringa oggetti, quindi il compilatore Kotlin è abbastanza intelligente da sapere che anche il parametro sarà a Stringa genere. 

Il esso Nome dell'argomento

Possiamo anche semplificare ulteriormente l'espressione lambda sostituendo l'argomento espressione lambda con il nome dell'argomento predefinito generato automaticamente esso.

stringList.last it.length == 3

Il esso il nome dell'argomento è stato generato automaticamente perché scorso può accettare un'espressione lambda o una funzione anonima (ci arriveremo a breve) con un solo argomento, e il suo tipo può essere dedotto dal compilatore.  

Ritorno locale in Lambda Expressions

Iniziamo con un esempio. Nel codice qui sotto, passiamo un'espressione lambda al per ciascuno() funzione invocata su IntList collezione. Questa funzione eseguirà il loop della raccolta ed eseguirà il lambda su ciascun elemento nell'elenco. Se qualche elemento è divisibile per 2, si fermerà e ritornerà dal lambda. 

fun surroundingFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return println ("End of surroundingFunction ()") surroundingFunction ( ) // non è successo niente

L'esecuzione del codice sopra potrebbe non aver dato il risultato che ci si sarebbe potuti aspettare. Questo perché quell'istruzione return non ritorna dal lambda ma invece dalla funzione contenente surroundingFunction ()! Ciò significa che l'ultima istruzione del codice nel file surroundingFunction () non verrà eseguito. 

// ... println ("End of surroundingFunction ()") // Questo non verrà eseguito // ... 

Per risolvere questo problema, dobbiamo dire in modo esplicito da quale funzione tornare usando un'etichetta o un nome. 

fun surroundingFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return @ forEach println ("Fine di surroundingFunction ()") / / Ora eseguirà surroundingFunction () // print "End of surroundingFunction ()"

Nel codice aggiornato sopra, abbiamo specificato il tag predefinito @per ciascuno subito dopo il ritorno parola chiave all'interno del lambda. Ora abbiamo dato istruzioni al compilatore di tornare dalla lambda al posto della funzione di contenimento surroundingFunction (). Ora l'ultima affermazione di surroundingFunction () verrà eseguito. 

Si noti che possiamo anche definire la nostra etichetta o il nostro nome. 

 // ... intList.forOgni myLabel @ if (it% 2 == 0) return @ myLabel // ... 

Nel codice sopra, abbiamo definito la nostra etichetta personalizzata chiamata myLabel @ e quindi specificato per il ritorno parola chiave. Il @per ciascuno etichetta generata dal compilatore per il per ciascuno la funzione non è più disponibile perché abbiamo definito la nostra. 

Tuttavia, vedremo presto come questo problema di ritorno locale può essere risolto senza etichette quando discutiamo a breve di funzioni anonime in Kotlin.

3. Funzioni membro

Questo tipo di funzione è definito all'interno di una classe, oggetto o interfaccia. L'utilizzo delle funzioni membro ci aiuta a modulare ulteriormente i nostri programmi. Vediamo ora come creare una funzione membro.

class Circle fun calculateArea (raggio: Double): Double require (raggio> 0, "Raggio deve essere maggiore di 0") return Math.PI * Math.pow (raggio, 2.0)

Questo snippet di codice mostra una classe Cerchio (discuteremo le classi di Kotlin nei post successivi) che ha una funzione membro calculateArea (). Questa funzione accetta un parametro raggio per calcolare l'area di un cerchio.

Per richiamare una funzione membro, usiamo il nome della classe contenente o istanza di oggetto con un punto, seguito dal nome della funzione, passando qualsiasi argomento se necessario.

val circle = Circle () print (circle.calculateArea (4.5)) // stamperà "63.61725123519331"

4. Funzioni anonime

Una funzione anonima è un altro modo per definire un blocco di codice che può essere passato a una funzione. Non è associato a nessun identificatore. Ecco le caratteristiche di una funzione anonima in Kotlin:

  • non ha nome
  • è stato creato con il divertimento parola chiave
  • contiene un corpo di funzione
val stringList: List = listOf ("in", "the", "club") print (stringList.last it.length == 3) // stamperà "il"

Perché abbiamo passato un lambda al scorso() funzione sopra, non possiamo essere espliciti sul tipo di ritorno. Per essere espliciti sul tipo di reso, dobbiamo invece utilizzare una funzione anonima.

val strLenThree = stringList.last (fun (string): Boolean return string.length == 3) print (strLenThree) // stampa "il"

Nel codice precedente, abbiamo sostituito l'espressione lambda con una funzione anonima perché vogliamo essere espliciti sul tipo di ritorno. 

Verso la fine della sezione lambda in questo tutorial, abbiamo usato un'etichetta per specificare da quale funzione tornare. Usando una funzione anonima invece di un lambda dentro per ciascuno() la funzione risolve questo problema più semplicemente. L'espressione di ritorno ritorna dalla funzione anonima e non da quella circostante, che nel nostro caso è surroundingFunction ().

fun surroundingFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach (fun (number) if (number% 2 == 0) return) println ("Fine di surroundingFunction ( ) ") // istruzione eseguita surroundingFunction () // stamperà" End of surroundingFunction () "

5. Funzioni locali o nidificate

Per portare ulteriormente la modularizzazione del programma, Kotlin ci fornisce le funzioni locali, note anche come funzioni annidate. Una funzione locale è una funzione dichiarata all'interno di un'altra funzione. 

fun printCircumferenceAndArea (raggio: Double): Unit fun calCircumference (raggio: Double): Double = (2 * Math.PI) * raggio val circumference = "% .2f" .format (calCircumference (radius)) fun calArea (raggio: Double): Double = (Math.PI) * Math.pow (raggio, 2.0) val area = "% .2f" .format (calArea (raggio)) print ("La circonferenza del cerchio del raggio del raggio $ è $ circonferenza e area è $ area ") printCircumferenceAndArea (3.0) // La circonferenza del cerchio del raggio 3.0 è 18,85 e l'area è 28,27

Come puoi osservare nel frammento di codice sopra, abbiamo due funzioni a linea singola: calCircumference () e calArea () annidato dentro il printCircumferenceAndAread () funzione. Le funzioni nidificate possono essere chiamate solo dall'interno della funzione di chiusura e non dall'esterno. Ancora una volta, l'uso di funzioni annidate rende il nostro programma più modulare e ordinato. 

Possiamo rendere più concise le nostre funzioni locali evitando di passare esplicitamente dei parametri. Questo è possibile perché le funzioni locali hanno accesso a tutti i parametri e le variabili della funzione di chiusura. Vediamo che ora in azione:

fun printCircumferenceAndArea (raggio: Double): Unit fun calCircumference (): Double = (2 * Math.PI) * raggio val circumference = "% .2f" .format (calCircumference ()) fun calArea (): Double = (Math .PI) * Math.pow (raggio, 2.0) val area = "% .2f" .format (calArea ()) // ...

Come puoi vedere, questo codice aggiornato sembra più leggibile e riduce il rumore che avevamo prima. Sebbene la funzione di inclusione in questo esempio sia piccola, in una funzione di chiusura più grande che può essere scomposta in funzioni annidate più piccole, questa funzione può davvero tornare utile. 

6. Funzioni Infix

Il infisso la notazione ci consente di chiamare facilmente una funzione membro o una funzione di estensione a un argomento. Oltre a una funzione che rappresenta un argomento, devi anche definire la funzione utilizzando il comando infisso modificatore. Per creare una funzione infisso, sono coinvolti due parametri. Il primo parametro è l'oggetto obiettivo, mentre il secondo parametro è solo un singolo parametro passato alla funzione. 

Creazione di una funzione membro Infix

Diamo un'occhiata a come creare una funzione infisso in una classe. Nell'esempio di codice seguente, abbiamo creato a Alunno classe con un mutabile kotlinScore campo istanza. Abbiamo creato una funzione infix usando il infisso modificatore prima del divertimento parola chiave. Come puoi vedere di seguito, abbiamo creato una funzione infisso addKotlinScore () che prende un punteggio e aggiunge al kotlinScore campo di istanza. 

class Student var kotlinScore = 0.0 infix fun addKotlinScore (punteggio: Double): Unit this.kotlinScore = kotlinScore + score

Chiamare una funzione Infix

Vediamo anche come richiamare la funzione infisso che abbiamo creato. Per chiamare una funzione infisso in Kotlin, non è necessario utilizzare la notazione punto e non è necessario avvolgere il parametro con parentesi. 

studente val = studente () studente addKotlinScore 95,00 stampa (student.kotlinScore) // stamperà "95.0"

Nel codice sopra, abbiamo chiamato la funzione infisso, l'oggetto di destinazione è alunno, e il doppio 95.00 è il parametro passato alla funzione. 

Usare saggiamente le funzioni infix può rendere il nostro codice più espressivo e più chiaro del normale stile. Ciò è molto apprezzato durante la scrittura dei test unitari in Kotlin (discuteremo dei test in Kotlin in un prossimo post).

"Chike" dovrebbe iniziareWith ("ch") myList dovrebbe contenere (myElement) "Chike" dovrebbe avereLunghezza (5) myMap dovrebbe avereKey (myKey) 

Il a Funzione Infix

In Kotlin, possiamo fare la creazione di a Paio esempio più succinto usando il a funzione infisso al posto di Paio costruttore. (Dietro le quinte, a crea anche a Paio istanza.) Si noti che il a la funzione è anche una funzione di estensione (ne discuteremo di più nel prossimo post).

infisso pubblico divertente  A.to (quello: B): coppia = Coppia (questo, quello)

Paragoniamo ora la creazione di a Paio istanza utilizzando entrambi i a funzione infix e direttamente usando il Paio costruttore, che esegue la stessa operazione e vede quale è migliore.

val nigeriaCallingCodePair = Da 234 a "Nigeria" val nigeriaCallingCodePair2 = Coppia (234, "Nigeria") // Come sopra

Come puoi vedere nel codice sopra, usa il a la funzione infisso è più concisa rispetto all'uso diretto di Paio costruttore per creare un Paio esempio. Ricorda che usando il a funzione infisso, 234 è l'oggetto target e il Stringa "Nigeria" è il parametro passato alla funzione. Inoltre, si noti che possiamo anche farlo per creare un Paio genere:

val nigeriaCallingCodePair3 = 234.to ("Nigeria") // come usare 234 in "Nigeria"

Nel post degli intervalli e delle raccolte, abbiamo creato una raccolta di mappe in Kotlin dandole un elenco di coppie: il primo valore è la chiave e il secondo il valore. Confrontiamo anche la creazione di una mappa usando entrambi a funzione infisso e il Paio costruttore per creare le singole coppie.

val callingCodesMap: Map = mapOf (da 234 a "Nigeria", da 1 a "USA", da 233 a "Ghana")

Nel codice sopra, abbiamo creato un elenco separato da virgole di Paio tipi usando il a funzione infissa e li ha passati al mapof () funzione. Possiamo anche creare la stessa mappa usando direttamente il Paio costruttore per ogni coppia.

val callingCodesPairMap: Map = mapOf (coppia (234, "Nigeria"), coppia (1, "USA"), coppia (233, "Ghana"))

Come puoi vedere di nuovo, attenersi al a la funzione infisso ha meno rumore rispetto all'utilizzo del Paio costruttore. 

Conclusione

In questo tutorial, hai imparato alcune delle cose interessanti che puoi fare con le funzioni di Kotlin. Abbiamo coperto:

  • funzioni di primo livello
  • espressioni lambda o funzioni letterali
  • funzioni membro
  • funzioni anonime
  • funzioni locali o annidate
  • funzioni infix

Ma non è tutto! C'è ancora molto da imparare sulle funzioni di Kotlin. Quindi, nel prossimo post, imparerai alcuni usi avanzati delle funzioni, come le funzioni di estensione, le funzioni di ordine superiore e le chiusure. A presto!

Per saperne di più sulla lingua di Kotlin, ti consiglio di visitare la documentazione di Kotlin. Oppure guarda alcuni dei nostri altri post di sviluppo di app per Android qui su Envato Tuts+!