Kotlin de la zero mai distractiv cu funcții

Kotlin este un limbaj de programare modern care se compilează la Java bytecode. Este gratuit și open source, și promite să facă codificarea pentru Android chiar mai distractiv.  

În articolul precedent, ați aflat despre pachete și funcții de bază în Kotlin. Funcțiile se află în centrul orașului Kotlin, astfel că în acest post ne vom uita mai atent. Vom explora următoarele funcții în Kotlin:

  • funcții de nivel superior
  • expresii lambda sau literali de funcții
  • funcții anonime
  • funcții locale sau imbricate
  • infix funcții
  • membre

Veți fi uimiți de toate lucrurile minunate pe care le puteți face cu funcțiile din Kotlin!

1. Funcții de nivel superior

Funcțiile de nivel superior sunt funcții în interiorul unui pachet Kotlin care sunt definite în afara oricărei clase, obiecte sau interfețe. Aceasta înseamnă că acestea sunt funcții pe care le apelați direct, fără a fi nevoie să creați obiecte sau să apelați nici o clasă. 

Dacă sunteți un coder Java, știți că, în mod obișnuit, vom crea metode statice de utilitate în clasele de ajutor. Aceste clase de ajutoare nu fac nimic - nu au metode de stat sau instanță și acționează doar ca un container pentru metodele statice. Un exemplu tipic este Colecții clasa în java.util pachetul și metodele sale statice. 

Funcțiile de nivel superior din Kotlin pot fi folosite ca înlocuitori pentru metodele de utilitate statică din clasele de ajutor pe care le codificăm în Java. Să ne uităm la definirea unei funcții de nivel superior în Kotlin. 

pachet com.chikekotlin.projectx.utils distracție checkUserStatus (): String return "online"

În codul de mai sus, am definit un pachet com.chikekotlin.projectx.utils în interiorul unui fișier numit UserUtils.kt și, de asemenea, a definit o funcție de utilitate de nivel superior numită checkUserStatus () în interiorul aceluiași pachet și fișier. Din fericire, această funcție foarte simplă returnează șirul "on-line". 

Următorul lucru pe care îl vom face este să utilizați această funcție utilitar într-un alt pachet sau fișier.

pachet com.chikekotlin.projectx.users import com.chikekotlin.projectx.utils.checkUserStatus dacă (checkUserStatus () == "online") // face ceva

În codul precedent, am importat funcția într-un alt pachet și apoi l-am executat! După cum puteți vedea, nu este necesar să creați un obiect sau să faceți referire la o clasă pentru a apela această funcție.

Interoperabilitate Java

Dat fiind că Java nu suportă funcții de nivel superior, compilatorul Kotlin din spatele scenei va crea o clasă Java, iar funcțiile individuale de nivel superior vor fi convertite în metode statice. În cazul nostru, clasa Java a fost generată UserUtilsKt cu o metodă statică checkUserStatus ()

/ * Java * / pachet com.chikekotlin.projectx.utils clasă publică UserUtilsKt public static String checkUserStatus () return "online"; 

Acest lucru înseamnă că apelanții Java pot apela pur și simplu metoda prin referirea la clasa generată, la fel ca pentru orice altă metodă statică.

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

Rețineți că putem schimba numele de clasă Java pe care compilatorul Kotlin îl generează utilizând @JvmName adnotare.

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

În codul de mai sus, am aplicat @JvmName adnotare și a specificat numele unei clase UserUtilspentru fișierul generat. Rețineți, de asemenea, că această adnotare este plasată la începutul fișierului Kotlin, înainte de definirea pachetului. 

Se poate face referire din Java astfel:

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

2. Expresii Lambda

Expresiile Lambda (sau literali de funcții) nu sunt, de asemenea, legate de nici o entitate, cum ar fi o clasă, un obiect sau o interfață. Ele pot fi transmise ca argumente altor funcții numite funcții de ordin superior (vom discuta mai multe despre acestea în următoarea postare). O expresie lambda reprezintă doar blocul unei funcții, iar utilizarea acestora reduce zgomotul din codul nostru. 

Dacă sunteți coder Java, știți că Java 8 și mai sus oferă suport pentru expresiile lambda. Pentru a folosi expresiile lambda într-un proiect care acceptă versiuni Java mai vechi, cum ar fi Java 7, 6 sau 5, putem folosi popularul bibliotecă Retrolambda. 

Unul dintre lucrurile minunate despre Kotlin este că expresiile lambda sunt susținute din cutie. Deoarece lambda nu este acceptată în Java 6 sau 7, pentru Kotlin să interacționeze cu ea, Kotlin creează o clasă anonimă Java în spatele scenei. Dar rețineți că crearea unei expresii lambda în Kotlin este destul de diferită decât în ​​Java.

Iată caracteristicile unei expresii lambda în Kotlin:

  • Trebuie să fie înconjurat de bretele curbate .
  • Nu are distracţie cuvinte cheie. 
  • Nu există modificator de acces (privat, public sau protejat), deoarece nu aparține niciunei clase, obiecte sau interfețe.
  • Nu are nume de funcții. Cu alte cuvinte, este anonim. 
  • Nu este specificat niciun tip de retur, deoarece va fi dedus de compilator.
  • Parametrii nu sunt înconjurați de paranteze ()

Și mai mult, putem atribui o expresie lambda unei variabile și apoi execută-o. 

Crearea expresiilor Lambda

Să vedem acum câteva exemple de expresii lambda. În codul de mai jos, am creat o expresie lambda fără parametri și i-am atribuit o variabilă mesaj. Apoi am executat expresia lambda sunând mesaj()

val mesaj = println ("Hei, Kotlin este foarte cool!") mesaj () // "Hei, Kotlin este foarte cool!"

Să vedem, de asemenea, cum să includem parametrii într-o expresie lambda. 

val mesaj = myString: String -> println (myString) mesaj ("Îmi place Kotlin") // "Îmi place Kotlin" ("Cât de departe?") // "Cât de departe?"

În codul de mai sus, am creat o expresie lambda cu parametrul myString, împreună cu tipul de parametru Şir. După cum puteți vedea, în fața tipului de parametru, există o săgeată: aceasta se referă la corpul lambda. Cu alte cuvinte, această săgeată separă lista de parametri de corpul lambda. Pentru a face mai concis, putem ignora complet tipul de parametru (deja dedus de compilator). 

val mesaj = myString -> println (myString) // va mai fi compilat

Pentru a avea mai mulți parametri, le separăm doar cu o virgulă. Și rețineți că nu împachetăm lista de parametri în paranteze ca în Java. 

val addNumbers = număr1: Int, numărul2: Int -> println ("Adăugarea $ number1 și $ number2") val result = number1 + number2 println ("

Totuși, rețineți că dacă tipurile de parametru nu pot fi deduse, ele trebuie specificate explicit (ca în acest exemplu), altfel codul nu va fi compilat.

Adăugarea rezultatelor 1 și 3 Rezultatul este 4

Trecerea lui Lambdas la funcții

Putem transmite expresii lambda ca parametri pentru funcții: acestea se numesc "funcții de ordin superior", deoarece sunt funcții de funcții. Aceste tipuri de funcții pot accepta o funcție lambda sau o funcție anonimă ca parametru: de exemplu, ultimul() funcție de colectare. 

În codul de mai jos, am trecut într-o expresie lambda la ultimul() funcţie. (Dacă doriți o actualizare a colecțiilor din Kotlin, vizitați cel de-al treilea tutorial din această serie) După cum spune numele, acesta returnează ultimul element din listă.  ultimul() acceptă o expresie lambda ca parametru, iar această expresie, la rândul său, ia un argument de tip Şir. Organismul său funcțional servește ca predicat pentru a căuta într-un subset de elemente din colecție. Aceasta înseamnă că expresia lambda va decide care elemente ale colecției vor fi luate în considerare atunci când o caută pe ultima.

val stringList: Listă = listOf ("in", "the", "club") print (stringList.last ()) // va imprima "club" print (stringList.last (s: String -> s.length == 3) ) // va imprima "

Să vedem cum să facem ca ultimul rând de cod de mai sus să fie mai ușor de citit.

stringList.last s: String -> s.length == 3 // va compila si imprima si "the" 

Compilatorul Kotlin ne permite să eliminăm parantezele funcției dacă ultimul argument din funcție este o expresie lambda. După cum puteți observa în codul de mai sus, ni sa permis să facem acest lucru, deoarece ultimul și singurul argument a fost transmis ultimul() este o expresie lambda. 

Mai mult, putem elimina tipul de parametru mai concis.

stringList.last s -> s.length == 3 // va compila, de asemenea, imprimarea "the" 

Nu este necesar să specificăm explicit tipul de parametru, deoarece tipul de parametru este întotdeauna același cu tipul elementului de colecție. În codul de mai sus, sunăm ultimul pe o colecție de listă Şir obiecte, astfel încât compilatorul Kotlin este suficient de inteligent pentru a ști că parametrul va fi de asemenea a Şir tip. 

aceasta Numele Argumentului

Putem chiar să simplificăm din nou expresia lambda înlocuind argumentul expresiei lambda cu numele argumentului implicit generat automat aceasta.

stringList.last it.length == 3

aceasta numele argumentului a fost generat automat deoarece ultimul poate accepta o expresie lambda sau o funcție anonimă (vom ajunge la asta în curând) cu un singur argument și tipul său poate fi dedus de compilator.  

Returnarea locală în expresiile Lambda

Să începem cu un exemplu. În codul de mai jos, trecem la o expresie lambda pentru fiecare() funcția invocată pe intList Colectie. Această funcție va trece prin colecție și va executa lambda pe fiecare element din listă. Dacă un element este divizibil cu 2, acesta se va opri și va reveni din lambda. 

Funcția în jurul Funcției () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return println ("End ofroundFunction ) // nu s-a intamplat nimic

Este posibil ca rularea codului de mai sus să nu fi dat rezultatul pe care l-ați fi așteptat. Acest lucru se datorează faptului că declarația de returnare nu se va întoarce din lambda, ci din funcția care conține surroundingFunction ()! Aceasta înseamnă că ultima instrucțiune de cod din surroundingFunction () nu va executa. 

// ... println ("End ofFunction ()") // Aceasta nu va executa // ... 

Pentru a rezolva această problemă, trebuie să-i spunem în mod explicit la ce funcție să revenim folosind o etichetă sau o etichetă de nume. 

distracție înconjurătoareFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return @ forEach println / Acum, se va executa înconjurătoareFunction () // print "End ofroundFunction ()"

În codul actualizat de mai sus, am specificat eticheta implicită @pentru fiecare imediat după întoarcere cuvinte cheie din interiorul lambda. Acum am instruit compilatorul să se întoarcă de la lambda în locul funcției care conține surroundingFunction (). Acum ultima declarație din surroundingFunction () va executa. 

Rețineți că putem, de asemenea, să definim eticheta proprie sau eticheta de nume. 

 // ... intList.forEach myLabel @ if (it% 2 == 0) retur @ myLabel // ... 

În codul de mai sus, am definit eticheta personalizată numită mylabel @ și apoi a specificat-o pentru întoarcere cuvinte cheie. @pentru fiecare eticheta generată de compilator pentru pentru fiecare funcția nu mai este disponibilă deoarece am definit propriul nostru. 

Cu toate acestea, veți vedea în curând cum poate fi rezolvată această problemă de returnare locală fără etichete atunci când discutăm în scurt timp despre funcțiile anonime din Kotlin.

3. Funcțiile membrilor

Acest tip de funcție este definit într-o clasă, obiect sau interfață. Utilizarea funcțiilor membrilor ne ajută să ne modularizăm în continuare programele. Să vedem acum cum să creăm o funcție membră.

clasa Circle fun calculateArea (raza: Dubla): Dubla necesita (raza> 0, "Radiusul trebuie sa fie mai mare de 0") returnare Math.PI * Math.pow (radius, 2.0)

Acest fragment de cod afișează o clasă Cerc (vom discuta despre cursurile Kotlin în posturi ulterioare) care are o funcție membră calculateArea (). Această funcție are un parametru rază pentru a calcula aria unui cerc.

Pentru a invoca o funcție de membru, vom folosi numele instanței de clasă sau obiect cu un punct, urmat de numele funcției, trecând orice argument dacă este necesar.

val circle = cerc () print (circle.calculateArea (4.5)) // va imprima "63.61725123519331"

4. Funcții anonime

O funcție anonimă este un alt mod de a defini un bloc de cod care poate fi transmis unei funcții. Nu este obligat la nici un identificator. Iată caracteristicile unei funcții anonime în Kotlin:

  • nu are nume
  • este creat cu distracţie cuvinte cheie
  • conține un corp de funcție
val stringList: Listă = listOf ("in", "cel", "clubul") print (stringList.last it.length == 3) // va imprima "

Pentru că am trecut o lambda la ultimul() funcția de mai sus, nu putem fi explicit despre tipul de returnare. Pentru a fi explicit despre tipul de returnare, trebuie să folosim o funcție anonimă.

val strLenThree = stringList.last (fun (șir): Boolean return string.length == 3) print (strLenThree) // va imprima "

În codul de mai sus, am înlocuit expresia lambda cu o funcție anonimă, deoarece vrem să fim explicit despre tipul de returnare. 

Spre sfârșitul secțiunii lambda din acest tutorial am folosit o etichetă pentru a specifica care dintre funcții să se întoarcă. Folosind o funcție anonimă în loc de o lambda înăuntru pentru fiecare() funcția rezolvă această problemă mai simplu. Expresia de întoarcere revine din funcția anonimă și nu din cea din jur, ceea ce este în cazul nostru surroundingFunction ().

distracție înconjurătoareFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach (distracție (număr) if (număr% 2 == 0) return println ) ") // declarație executată înconjurătoareFunction () // va imprima" End ofroundFunction () "

5. Funcții locale sau neschimbate

Pentru a lua mai departe modularizarea programelor, Kotlin ne oferă funcții locale - cunoscute și ca funcții imbricate. O funcție locală este o funcție declarată în interiorul unei alte funcții. 

distracție printCircumferenceAndArea (rază: dublă): unitate fun calCircumferință (rază: dublă): dublă = (2 * Math.PI) * radius circum circumference = "% .2f" .format (calCircumference (radius) ("Raza de raza") este circumferinta $ si circumferinta este suprafața $ ") printCircumferenceAndArea (3.0) // Circumferința circulară de 3.0 rază este 18.85 și aria este 28.27

După cum puteți observa în fragmentul de cod de mai sus, avem două funcții cu o singură linie: calCircumference () și calArea () imbricat în interior printCircumferenceAndAread () funcţie. Funcțiile imbricate pot fi apelate numai din interiorul funcției de închidere și nu din exterior. Din nou, utilizarea funcțiilor imbricate face ca programul nostru să fie mai modular și mai ordonat. 

Putem face funcțiile noastre locale mai concise prin faptul că nu le transmitem în mod explicit parametrii. Acest lucru este posibil deoarece funcțiile locale au acces la toți parametrii și variabilele funcției de închidere. Să vedem asta acum în acțiune:

distracție printCircumferenceAndArea (rază: dublă): Unitate fun calCircumference (): Double = (2 * Math.PI) * radius val circumference = "% .2f" .format (calCircumference .PI) * Math.pow (raza, 2.0) val area = "% .2f" .format (calArea ()) // ...

După cum puteți vedea, acest cod actualizat arată mai ușor de citit și reduce zgomotul pe care l-am avut înainte. Deși funcția de închidere din acest exemplu este mică, într-o funcție de închidere mai mare, care poate fi împărțită în funcții imbricate mai mici, această caracteristică poate fi cu adevărat utilă. 

6. Funcțiile infix

infix notația ne permite să apelam cu ușurință o funcție membră cu un argument sau o funcție de extensie. În afară de o funcție care este un argument, trebuie să definiți funcția folosind infix modificator. Pentru a crea o funcție infix, sunt implicați doi parametri. Primul parametru este obiectul țintă, în timp ce al doilea parametru este doar un singur parametru trecut la funcție. 

Crearea unei funcții membru Infix

Să ne uităm la modul de creare a unei funcții infix într-o clasă. În exemplul de cod de mai jos, am creat o Student clasa cu un mutable kotlinScore câmp instanță. Am creat o funcție infix folosind infix modificator înainte de distracţie cuvinte cheie. După cum puteți vedea mai jos, am creat o funcție infix addKotlinScore () care ia un scor și adaugă la kotlinScore instanță. 

clasa Student var kotlinScore = 0.0 infix distracție addKotlinScore (scor: dublu): Unit this.kotlinScore = kotlinScore + scor

Apelarea unei funcții infix

Să vedem, de asemenea, cum să invocăm funcția infix pe care am creat-o. Pentru a apela o funcție infix în Kotlin, nu este nevoie să folosim notația punctului și nu trebuie să înfășurăm parametrul cu paranteze. 

val student = Student () student addKotlinScore 95.00 print (student.kotlinScore) // va imprima "95.0"

În codul de mai sus, am numit funcția infix, obiectul țintă este student, și dublu 95.00 este parametrul transmis funcției. 

Folosind cu ușurință funcțiile infix, codul nostru poate fi mai expresiv și mai clar decât stilul normal. Acest lucru este foarte apreciat atunci când scriem teste unitate în Kotlin (vom discuta despre testarea în Kotlin într-un post viitor).

"Chike" ar trebui să înceapă Cu ("ch") MyList ar trebui să conțină (myElement) "Chike" ar trebui să aibăLength (5) myMap ar trebui să haveKey (myKey) 

la Funcția Infix

În Kotlin, putem face crearea unui Pereche exemplu mai succint prin utilizarea la infix funcția în loc de Pereche constructor. (În spatele scenelor, la creează și o Pereche exemplu). Rețineți că la funcția este, de asemenea, o funcție de extindere (vom discuta mai multe despre acestea în următoarea postare).

public infix distracție  A.to (că: B): Pereche = Pereche (asta, asta)

Să comparăm crearea a Pereche exemplu folosind ambele la infix funcția și direct folosind Pereche constructor, care efectuează aceeași operațiune, și a vedea care este mai bine.

val nigeriaCallingCodePair = 234 la "Nigeria" val nigeriaCallingCodePair2 = Perechea (234, "Nigeria") // Același lucru de mai sus

După cum puteți vedea în codul de mai sus, folosind la funcția infix este mai concisă decât utilizarea directă Pereche constructor pentru a crea un Pereche instanță. Amintiți-vă că folosind la infix funcție, 234 este obiectul țintă și Şir "Nigeria" este parametrul transmis funcției. Mai mult decât atât, rețineți că putem face acest lucru și pentru a crea o Pereche tip:

val nigeriaCallingCodePair3 = 234.to ("Nigeria") // la fel ca folosind 234 la "Nigeria"

În postul Ranges and Collections, am creat o colecție de hărți în Kotlin, oferindu-i o listă de perechi - prima valoare fiind cheia, iar a doua valoare. Să comparăm, de asemenea, crearea unei hărți folosind ambele la infix și funcția Pereche constructor pentru a crea perechi individuale.

val callingCodesMap: Harta = mapOf (234 la "Nigeria", 1 la "SUA", 233 la "Ghana")

În codul de mai sus, am creat o listă separată prin virgulă Pereche tipuri folosind la infix funcția și le-a trecut la harta() funcţie. De asemenea, putem crea aceeași hartă utilizând direct Pereche constructor pentru fiecare pereche.

val apelareaCodesPairMap: Harta = mapOf (pereche (234, "Nigeria"), pereche (1, "SUA"), pereche (233, "Ghana"))

După cum puteți vedea din nou, lipirea cu la infix funcția are mai puțin zgomot decât utilizarea Pereche constructor. 

Concluzie

În acest tutorial, ați aflat despre unele lucruri interesante pe care le puteți face cu funcțiile din Kotlin. Am acoperit:

  • funcții de nivel superior
  • expresii lambda sau literali de funcții
  • membre
  • funcții anonime
  • funcții locale sau imbricate
  • infix funcții

Dar asta nu este tot! Mai sunt multe de învățat despre funcțiile din Kotlin. Deci, în postul următor, veți învăța câteva utilizări avansate ale funcțiilor, cum ar fi funcții de extensie, funcții de ordin superior și închideri. Ne vedem în curând!

Pentru a afla mai multe despre limba Kotlin, vă recomand să vizitați documentația Kotlin. Sau verificați câteva dintre celelalte postări pentru dezvoltarea aplicațiilor Android aici pe Envato Tuts+!

Cod