Kotlin From Scratch Mehr Spaß mit Funktionen

Kotlin ist eine moderne Programmiersprache, die Java-Bytecode kompiliert. Es ist kostenlos und Open Source und verspricht, die Kodierung für Android noch mehr Spaß zu machen.  

Im vorigen Artikel haben Sie sich mit Paketen und grundlegenden Funktionen in Kotlin vertraut gemacht. Funktionen sind das Herzstück von Kotlin. In diesem Beitrag werden wir sie genauer betrachten. Wir werden die folgenden Arten von Funktionen in Kotlin untersuchen:

  • Funktionen auf oberster Ebene
  • Lambda-Ausdrücke oder Funktionsliterale
  • anonyme Funktionen
  • lokale oder verschachtelte Funktionen
  • Infix-Funktionen
  • Mitgliedsfunktionen

Sie werden erstaunt sein, was alles, was Sie mit Funktionen in Kotlin tun können, für Sie interessant ist!

1. Funktionen auf oberster Ebene

Funktionen der obersten Ebene sind Funktionen innerhalb eines Kotlin-Pakets, die außerhalb von Klassen, Objekten oder Schnittstellen definiert sind. Dies bedeutet, dass es sich um Funktionen handelt, die Sie direkt aufrufen, ohne dass Sie ein Objekt erstellen oder eine Klasse aufrufen müssen. 

Wenn Sie ein Java-Codierer sind, wissen Sie, dass wir statische Methoden für Hilfsprogramme normalerweise in Hilfsklassen erstellen. Diese Hilfsklassen machen eigentlich nichts - sie haben keine Zustands- oder Instanzmethoden und dienen lediglich als Container für die statischen Methoden. Ein typisches Beispiel ist das Sammlungen Klasse in der java.util Paket und seine statischen Methoden. 

Die Funktionen der obersten Ebene in Kotlin können als Ersatz für die statischen Hilfsprogrammmethoden in Hilfsklassen verwendet werden, die wir in Java codieren. Sehen wir uns an, wie Sie eine Top-Level-Funktion in Kotlin definieren. 

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

Im obigen Code haben wir ein Paket definiert com.chikekotlin.projectx.utils in einer Datei namens UserUtils.kt und definierte auch eine aufgerufene Dienstprogrammfunktion der obersten Ebene checkUserStatus () in demselben Paket und derselben Datei. Der Einfachheit halber gibt diese sehr einfache Funktion den String "online" zurück.. 

Als nächstes verwenden wir diese Utility-Funktion in einem anderen Paket oder einer anderen Datei.

Paket com.chikekotlin.projectx.users importiert com.chikekotlin.projectx.utils.checkUserStatus if (checkUserStatus () == "online") // etwas tun

Im vorigen Code haben wir die Funktion in ein anderes Paket importiert und dann ausgeführt! Wie Sie sehen können, müssen wir kein Objekt erstellen oder auf eine Klasse verweisen, um diese Funktion aufzurufen.

Java-Interoperabilität

Da Java keine Funktionen der obersten Ebene unterstützt, erstellt der Kotlin-Compiler im Hintergrund eine Java-Klasse, und die einzelnen Funktionen der obersten Ebene werden in statische Methoden konvertiert. In unserem eigenen Fall wurde die Java-Klasse generiert UserUtilsKt mit einer statischen Methode checkUserStatus ()

/ * Java * / package com.chikekotlin.projectx.utils öffentliche Klasse UserUtilsKt public static String checkUserStatus () return "online"; 

Dies bedeutet, dass Java-Aufrufer die Methode einfach aufrufen können, indem sie auf ihre generierte Klasse verweisen, genau wie bei jeder anderen statischen Methode.

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

Beachten Sie, dass wir den Java-Klassennamen ändern können, den der Kotlin-Compiler mithilfe von generiert @JvmName Anmerkung.

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

Im obigen Code haben wir die @JvmName Anmerkung und gab einen Klassennamen an UserUtilsfür die generierte Datei. Beachten Sie auch, dass sich diese Anmerkung vor der Paketdefinition am Anfang der Kotlin-Datei befindet. 

Es kann wie folgt von Java aus referenziert werden:

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

2. Lambda-Ausdrücke

Lambda-Ausdrücke (oder Funktionsliterale) sind auch nicht an Entitäten wie Klassen, Objekte oder Schnittstellen gebunden. Sie können als Argumente an andere Funktionen übergeben werden, die Funktionen höherer Ordnung genannt werden (wir werden diese im nächsten Beitrag ausführlicher besprechen). Ein Lambda-Ausdruck stellt nur den Block einer Funktion dar und deren Verwendung reduziert das Rauschen in unserem Code. 

Wenn Sie ein Java-Codierer sind, wissen Sie, dass Java 8 und höher Unterstützung für Lambda-Ausdrücke bietet. Zur Verwendung von Lambda-Ausdrücken in einem Projekt, das frühere Java-Versionen wie Java 7, 6 oder 5 unterstützt, können wir die beliebte Retrolambda-Bibliothek verwenden. 

Eine der erstaunlichen Eigenschaften von Kotlin ist, dass Lambda-Ausdrücke standardmäßig unterstützt werden. Da Lambda in Java 6 oder 7 nicht unterstützt wird und Kotlin damit interoperiert, erstellt Kotlin hinter der Szene eine anonyme Java-Klasse. Beachten Sie jedoch, dass das Erstellen eines Lambda-Ausdrucks in Kotlin sich erheblich von Java unterscheidet.

Hier sind die Merkmale eines Lambda-Ausdrucks in Kotlin:

  • Es muss von geschweiften Klammern umgeben sein .
  • Es hat nicht die Spaß Stichwort. 
  • Es gibt keinen Zugriffsmodifizierer (privat, öffentlich oder geschützt), da er keiner Klasse, keinem Objekt oder keiner Schnittstelle angehört.
  • Es hat keinen Funktionsnamen. Mit anderen Worten, es ist anonym. 
  • Es wird kein Rückgabetyp angegeben, da er vom Compiler abgeleitet wird.
  • Parameter sind nicht in Klammern eingeschlossen ()

Darüber hinaus können wir einer Variablen einen Lambda-Ausdruck zuweisen und ihn dann ausführen. 

Lambda-Ausdrücke erstellen

Sehen wir uns nun einige Beispiele für Lambda-Ausdrücke an. Im folgenden Code haben wir einen Lambda-Ausdruck ohne Parameter erstellt und ihm eine Variable zugewiesen Botschaft. Wir haben dann den Lambda-Ausdruck durch Aufruf ausgeführt Botschaft()

val message = println ("Hey, Kotlin ist wirklich cool!") message () // "Hey, Kotlin ist wirklich cool!"

Sehen wir uns auch an, wie Parameter in einen Lambda-Ausdruck eingefügt werden. 

val message = myString: String -> println (myString) -Meldung ("Ich liebe Kotlin") // "Ich liebe Kotlin" -Meldung ("Wie weit?") // "Wie weit?"

Im obigen Code haben wir einen Lambda-Ausdruck mit dem Parameter erstellt myString, zusammen mit dem Parametertyp String. Wie Sie sehen, befindet sich vor dem Parametertyp ein Pfeil: Dieser bezieht sich auf den Lambda-Körper. Mit anderen Worten, dieser Pfeil trennt die Parameterliste vom Lambda-Körper. Um es übersichtlicher zu machen, können wir den Parametertyp, der vom Compiler bereits abgeleitet wurde, vollständig ignorieren.. 

val message = myString -> println (myString) // wird immer noch kompiliert

Um mehrere Parameter zu haben, trennen wir sie einfach mit einem Komma. Denken Sie daran, dass wir die Parameterliste nicht wie in Java in Klammern einschließen. 

val addNumbers = number1: Int, number2: Int -> println ("Hinzufügen von $ number1 und $ number2") val result = number1 + number2 println ("Das Ergebnis ist $ result") addNumbers (1, 3)

Beachten Sie jedoch, dass, wenn die Parametertypen nicht abgeleitet werden können, diese explizit angegeben werden müssen (wie in diesem Beispiel), andernfalls wird der Code nicht kompiliert.

Addieren von 1 und 3 Das Ergebnis ist 4

Lambdas an Funktionen übergeben

Wir können Lambda-Ausdrücke als Parameter an Funktionen übergeben: Diese werden "Funktionen höherer Ordnung" genannt, da sie Funktionen von Funktionen sind. Diese Arten von Funktionen können ein Lambda oder eine anonyme Funktion als Parameter akzeptieren: zum Beispiel die zuletzt() Sammlungsfunktion. 

Im folgenden Code haben wir einen Lambda-Ausdruck an den übergeben zuletzt() Funktion. (Wenn Sie eine Auffrischung der Sammlungen in Kotlin erhalten möchten, besuchen Sie das dritte Tutorial dieser Serie.) Wie der Name sagt, wird das letzte Element in der Liste zurückgegeben.  zuletzt() akzeptiert einen Lambda-Ausdruck als Parameter, und dieser Ausdruck nimmt wiederum ein Argument vom Typ String. Sein Funktionskörper dient als Prädikat für die Suche innerhalb einer Teilmenge von Elementen in der Auflistung. Das bedeutet, dass der Lambda-Ausdruck entscheidet, welche Elemente der Sammlung bei der Suche nach dem letzten berücksichtigt werden.

val stringList: Liste = listOf ("in", "the", "club") print (stringList.last ()) // druckt "club" print (stringList.last (s: String -> s.length == 3) ) // druckt "the"

Mal sehen, wie die letzte Codezeile oben lesbarer wird.

stringList.last s: String -> s.length == 3 // wird auch "the" kompilieren und drucken 

Der Kotlin-Compiler ermöglicht das Entfernen der Funktionsklammern, wenn das letzte Argument der Funktion ein Lambda-Ausdruck ist. Wie Sie im obigen Code beobachten können, durften wir dies tun, da das letzte und einzige Argument an das übergeben wurde zuletzt() Funktion ist ein Lambda-Ausdruck. 

Außerdem können wir es durch das Entfernen des Parametertyps präziser gestalten.

stringList.last s -> s.length == 3 // kompiliert auch print "the" 

Der Parametertyp muss nicht explizit angegeben werden, da der Parametertyp immer dem Collection-Elementtyp entspricht. Im obigen Code rufen wir an zuletzt auf einer Listensammlung von String Objekte, so dass der Kotlin-Compiler intelligent genug ist, um zu wissen, dass der Parameter auch a ist String Art. 

Das es Argumentname

Wir können den Lambda-Ausdruck sogar noch weiter vereinfachen, indem Sie das Argument des Lambda-Ausdrucks durch den automatisch generierten Standardargumentnamen ersetzen es.

stringList.last it.length == 3

Das es Argumentname wurde automatisch generiert, weil zuletzt Ich kann einen Lambda-Ausdruck oder eine anonyme Funktion (wir werden dies in Kürze tun) mit nur einem Argument akzeptieren, und der Typ kann vom Compiler abgeleitet werden.  

Lokale Rückkehr in Lambda-Ausdrücken

Beginnen wir mit einem Beispiel. Im folgenden Code übergeben wir einen Lambda-Ausdruck an die für jeden() Funktion aufgerufen am intList Sammlung. Diese Funktion durchläuft die Sammlung und führt das Lambda für jedes Element in der Liste aus. Wenn ein Element durch 2 teilbar ist, stoppt es und kehrt vom Lambda zurück. 

fun aroundFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return println ("Ende der umgebendenFunktion ()") umschließendeFunktion ( ) // nichts ist passiert

Beim Ausführen des obigen Codes erhalten Sie möglicherweise nicht das erwartete Ergebnis. Dies liegt daran, dass diese return-Anweisung nicht vom Lambda, sondern von der enthaltenden Funktion zurückgegeben wird umgebendeFunktion ()! Dies bedeutet, dass die letzte Code-Anweisung in der umgebendeFunktion () wird nicht ausgeführt. 

//… println ("Ende der umgebendenFunktion ()") // Das wird nicht ausgeführt // 

Um dieses Problem zu beheben, müssen Sie explizit angeben, von welcher Funktion Sie zurückkehren, indem Sie ein Label oder ein Namens-Tag verwenden. 

fun aroundFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return @ forEach println ("Ende der umgebendenFunktion ()") / / Nun wird es ausgeführt aroundFunction () // print "End of aroundFunction ()"

In dem aktualisierten Code oben haben wir das Standard-Tag angegeben @für jeden unmittelbar nach dem Rückkehr Stichwort innerhalb des Lambda. Wir haben jetzt den Compiler angewiesen, von der Lambda-Funktion anstelle der übergeordneten Funktion zurückzukehren umgebendeFunktion (). Nun die letzte Aussage von umgebendeFunktion () wird ausführen. 

Beachten Sie, dass wir auch unser eigenes Label oder Namensschild definieren können. 

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

Im obigen Code haben wir unser benutzerdefiniertes Label definiert myLabel @ und spezifizierte es dann für Rückkehr Stichwort. Das @für jeden vom Compiler generiertes Label für die für jeden Funktion ist nicht mehr verfügbar, weil wir unsere eigene definiert haben. 

Sie werden jedoch bald feststellen, wie dieses lokale Rückkehrproblem ohne Labels gelöst werden kann, wenn wir in Kotlin anonyme Funktionen besprechen.

3. Mitgliedsfunktionen

Diese Art von Funktion wird in einer Klasse, einem Objekt oder einer Schnittstelle definiert. Die Verwendung von Mitgliedsfunktionen hilft uns dabei, unsere Programme weiter zu modularisieren. Lassen Sie uns nun sehen, wie Sie eine Memberfunktion erstellen.

class Circle fun berechneArea (Radius: Double): Double required (Radius> 0, "Radius muss größer als 0 sein") Rückgabe Math.PI * Math.pow (Radius, 2.0)

Dieses Code-Snippet zeigt eine Klasse Kreis (Wir werden Kotlin-Klassen in späteren Beiträgen besprechen), der eine Member-Funktion hat berechneFläche (). Diese Funktion benötigt einen Parameter Radius um die Fläche eines Kreises zu berechnen.

Um eine Member-Funktion aufzurufen, verwenden wir den Namen der enthaltenden Klasse oder Objektinstanz mit einem Punkt, gefolgt von dem Funktionsnamen, wobei ggf. Argumente übergeben werden.

val circle = Circle () print (circle.calculateArea (4.5)) // "63.61725123519331" wird gedruckt

4. Anonyme Funktionen

Eine anonyme Funktion ist eine weitere Möglichkeit, einen Codeblock zu definieren, der an eine Funktion übergeben werden kann. Es ist an keinen Bezeichner gebunden. Hier sind die Merkmale einer anonymen Funktion in Kotlin:

  • hat keinen Namen
  • wird mit dem erstellt Spaß Stichwort
  • enthält einen Funktionskörper
val stringList: Liste = listOf ("in", "the", "club") print (stringList.last it.length == 3) // druckt "the"

Weil wir ein Lambda an die übergeben haben zuletzt() Funktion oben können wir den Rückgabetyp nicht explizit angeben. Um den Rückgabetyp explizit zu beschreiben, müssen wir stattdessen eine anonyme Funktion verwenden.

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

Im obigen Code haben wir den Lambda-Ausdruck durch eine anonyme Funktion ersetzt, da wir den Rückgabetyp explizit angeben möchten. 

Gegen Ende des Lambda-Abschnitts in diesem Lernprogramm haben wir ein Label verwendet, um anzugeben, von welcher Funktion Sie zurückkehren möchten. Verwendung einer anonymen Funktion anstelle eines Lambda innerhalb des für jeden() Funktion löst dieses Problem einfacher. Der Rückgabeausdruck gibt die anonyme Funktion zurück und nicht die umgebende, was in unserem Fall der Fall ist umgebendeFunktion ().

fun aroundFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach (fun (number) if (number% 2 == 0) return) println ("Ende der umgebendenFunktion ( ) ") // Anweisung ausgeführt umgebungFunktion () // gibt" Ende von umgebungFunktion () "aus

5. Lokale oder geschachtelte Funktionen

Um die Modularisierung der Programme weiter voranzutreiben, stellt Kotlin lokale Funktionen bereit, die auch als verschachtelte Funktionen bezeichnet werden. Eine lokale Funktion ist eine Funktion, die in einer anderen Funktion deklariert ist. 

fun printCircumferenceAndArea (radius: Double): Einheit fun calCircumference (Radius: Double): Double = (2 * Math.PI) * radius val Umfang = "% .2f" .format (calCumgebun (Radius)) fun calArea (radius: Double): Double = (Math.PI) * Math.pow (Radius, 2.0) Wertebereich = "% .2f" .format (calArea (radius)) print ("Der Kreisumfang von $ radius radius ist $ umfang und area ist $ area ") printCircumferenceAndArea (3.0) // Der Kreisumfang des Radius 3.0 beträgt 18,85 und der Bereich 28,27

Wie Sie im obigen Code-Snippet sehen können, haben wir zwei einzeilige Funktionen: calCumfum () und calArea () verschachtelt in der printCircumferenceAndAread () Funktion. Die verschachtelten Funktionen können nur von der einschließenden Funktion und nicht von außerhalb aufgerufen werden. Durch die Verwendung geschachtelter Funktionen wird unser Programm modularer und aufgeräumter. 

Wir können unsere lokalen Funktionen präziser gestalten, indem wir ihnen nicht explizit Parameter übergeben. Dies ist möglich, weil lokale Funktionen auf alle Parameter und Variablen der umgebenden Funktion zugreifen können. Lassen Sie uns das jetzt in Aktion sehen:

fun printCircumferenceAndArea (radius: Double): Einheit fun calCircumference (): Double = (2 * Math.PI) * radius val Umfang = "% .2f" .format (calCircumference ()) fun calArea (): Double = (Math .PI) * Math.pow (radius, 2.0) Wertebereich = "% .2f" .format (calArea ()) //…

Wie Sie sehen, sieht dieser aktualisierte Code lesbarer aus und reduziert das Rauschen, das wir zuvor hatten. Obwohl die einschließende Funktion in diesem Beispiel klein ist, kann diese Funktion in einer größeren einschließenden Funktion, die in kleinere verschachtelte Funktionen unterteilt werden kann, sehr nützlich sein. 

6. Infix-Funktionen

Das Infix Notation ermöglicht uns den einfachen Aufruf einer Member-Funktion mit einem Argument oder einer Erweiterungsfunktion. Zusätzlich zu einer Funktion, die ein Argument ist, müssen Sie die Funktion auch mit definieren Infix Modifikator. Um eine Infix-Funktion zu erstellen, sind zwei Parameter beteiligt. Der erste Parameter ist das Zielobjekt, während der zweite Parameter nur ein einzelner Parameter ist, der an die Funktion übergeben wird. 

Erstellen einer Infix-Member-Funktion

Sehen wir uns an, wie Sie eine Infix-Funktion in einer Klasse erstellen. Im folgenden Codebeispiel haben wir eine erstellt Student Klasse mit einem veränderlichen kotlinScore Instanzfeld. Wir haben eine Infix-Funktion mit der Infix Modifikator vor dem Spaß Stichwort. Wie Sie unten sehen können, haben wir eine Infix-Funktion erstellt addKotlinScore () das nimmt eine Kerbe und addiert zum kotlinScore Instanzfeld. 

class Student var kotlinScore = 0.0 Infix Spaß addKotlinScore (Score: Doppel): Einheit this.kotlinScore = kotlinScore + score

Infix-Funktion aufrufen

Lassen Sie uns auch sehen, wie wir die von uns erstellte Infix-Funktion aufrufen. Um eine Infix-Funktion in Kotlin aufzurufen, müssen wir die Punktnotation nicht verwenden und den Parameter nicht in Klammern einschließen. 

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

Im obigen Code haben wir die Infix-Funktion genannt, das Zielobjekt ist Student, und das doppelte 95,00 ist der an die Funktion übergebene Parameter. 

Die weise Verwendung von Infix-Funktionen kann unseren Code ausdrucksvoller und klarer machen als der normale Stil. Dies wird sehr geschätzt, wenn Sie Unit-Tests in Kotlin schreiben (wir werden in Kotlin über Tests in einem späteren Beitrag sprechen)..

"Chike" sollte startWith ("ch") myList sollte (myElement) enthalten. "Chike" sollteLength (5). MyMap sollte haveKey (myKey) haben. 

Das zu Infix-Funktion

In Kotlin können wir die Erstellung einer Paar Instanz prägnanter mit der zu Infix-Funktion anstelle von Paar Konstrukteur. (Hinter den Kulissen, zu schafft auch ein Paar Instanz.) Beachten Sie, dass die zu Funktion ist auch eine Erweiterungsfunktion (wir werden diese im nächsten Post mehr besprechen).

öffentlicher Infix-Spaß  A.zu (das: B): Paar = Paar (das, das)

Vergleichen wir nun die Erstellung von a Paar Beispiel mit beiden zu Infix-Funktion und direkt mit der Paar Konstruktor, der dieselbe Operation ausführt, und sehen, welcher besser ist.

val nigeriaCallingCodePair = 234 nach "Nigeria" val nigeriaCallingCodePair2 = Paar (234, "Nigeria") // Wie oben

Wie Sie im obigen Code sehen können, verwenden Sie die zu Die Infix - Funktion ist übersichtlicher als die direkte Verwendung der Paar Konstruktor zum Erstellen eines Paar Beispiel. Denken Sie daran, dass die Verwendung der zu Infix-Funktion, 234 ist das Zielobjekt und das String "Nigeria" ist der Parameter, der an die Funktion übergeben wird. Beachten Sie außerdem, dass wir dies auch tun können, um eine Paar Art:

val nigeriaCallingCodePair3 = 234.to ("Nigeria") // wie bei der Verwendung von 234 für "Nigeria"

Im Beitrag "Bereiche und Sammlungen" haben wir in Kotlin eine Kartensammlung erstellt, indem wir ihm eine Liste von Paaren geben. Der erste Wert ist der Schlüssel und der zweite der Wert. Vergleichen wir auch die Erstellung einer Karte mithilfe der beiden zu Infix-Funktion und die Paar Konstruktor, um die einzelnen Paare zu erstellen.

val callingCodesMap: Karte = mapOf (234 nach "Nigeria", 1 nach "USA", 233 nach "Ghana")

Im obigen Code haben wir eine durch Kommas getrennte Liste von erstellt Paar Typen mit der zu Infix-Funktion und übergeben sie an die Karte von() Funktion. Wir können dieselbe Karte auch direkt erstellen, indem Sie die Paar Konstruktor für jedes Paar.

val callingCodesPairMap: Karte = mapOf (Paar (234, "Nigeria"), Paar (1, "USA"), Paar (233, "Ghana"))

Wie du wieder sehen kannst, bleib bei der zu Die Infix - Funktion ist geräuschärmer als die Verwendung der Paar Konstrukteur. 

Fazit

In diesem Lernprogramm haben Sie einige der coolen Funktionen kennengelernt, die Sie mit Funktionen in Kotlin machen können. Wir haben abgedeckt:

  • Funktionen auf oberster Ebene
  • Lambda-Ausdrücke oder Funktionsliterale
  • Mitgliedsfunktionen
  • anonyme Funktionen
  • lokale oder verschachtelte Funktionen
  • Infix-Funktionen

Aber das ist nicht alles! In Kotlin gibt es noch mehr Informationen zu Funktionen. Im nächsten Beitrag erfahren Sie einige fortgeschrittene Verwendungsmöglichkeiten von Funktionen wie Erweiterungsfunktionen, Funktionen höherer Ordnung und Schließungen. Bis bald!

Um mehr über die Kotlin-Sprache zu lernen, empfehle ich die Kotlin-Dokumentation. Auf Envato Tuts finden Sie auch einige unserer anderen Android-Apps zur Entwicklung von Apps+!