() translation by (you can also view the original English article)
Kotlin ist eine funktionale Sprache, und das bedeutet, dass Funktionen im Vordergrund stehen. Die Sprache ist vollgepackt mit Funktionen, die Codierungsfunktionen einfach und ausdrucksstark machen. In diesem Beitrag erfahren Sie mehr über Erweiterungsfunktionen, Funktionen höherer Ordnung, Schließungen und Inline-Funktionen in Kotlin.
Im vorherigen Artikel haben Sie Informationen zu Funktionen der obersten Ebene, Lambda-Ausdrücken, anonymen Funktionen, lokalen Funktionen, Infix-Funktionen und schließlich zu Mitgliedsfunktionen in Kotlin erhalten. In diesem Tutorial erfahren Sie mehr über Funktionen in Kotlin, indem wir uns mit folgenden Themen befassen:
- Erweiterungsfunktionen
- Funktionen höherer Ordnung
- Verschlüsse
- Inline-Funktionen
1. Erweiterungsfunktionen
Wäre es nicht schön, wenn der String
-Typ in Java eine Methode zum Großschreiben des ersten Buchstabens in einem String
hätte - wie ucfirst()
in PHP? Wir könnten diese Methode UpperCaseFirstLetter()
aufrufen.
Um dies zu realisieren, können Sie eine String
-Unterklasse erstellen, die den String
-Typ in Java erweitert. Denken Sie jedoch daran, dass die String
-Klasse in Java endgültig ist. Dies bedeutet, dass Sie sie nicht erweitern können. Eine mögliche Lösung für Kotlin wäre das Erstellen von Hilfsfunktionen oder Funktionen der obersten Ebene. Dies ist jedoch möglicherweise nicht ideal, da wir die Funktion zur automatischen Vervollständigung der IDE nicht verwenden können, um die Liste der für den String
-Typ verfügbaren Methoden anzuzeigen. Was wirklich schön wäre, wäre, einer Klasse irgendwie eine Funktion hinzuzufügen, ohne von dieser Klasse erben zu müssen.
Nun, Kotlin hat uns mit einer weiteren großartigen Funktion ausgestattet: Erweiterungsfunktionen. Diese geben uns die Möglichkeit, eine Klasse mit neuen Funktionen zu erweitern, ohne von dieser Klasse erben zu müssen. Mit anderen Worten, wir müssen keinen neuen Subtyp erstellen oder den ursprünglichen Typ ändern.
Eine Erweiterungsfunktion wird außerhalb der Klasse deklariert, die erweitert werden soll. Mit anderen Worten, es handelt sich auch um eine Funktion der obersten Ebene (wenn Sie eine Auffrischung der Funktionen der obersten Ebene in Kotlin wünschen, besuchen Sie das Tutorial Mehr Spaß mit Funktionen in dieser Reihe).
Neben Erweiterungsfunktionen unterstützt Kotlin auch Erweiterungseigenschaften. In diesem Beitrag werden wir Erweiterungsfunktionen diskutieren und bis zu einem zukünftigen Beitrag warten, um die Erweiterungseigenschaften zusammen mit Klassen in Kotlin zu diskutieren.
Erweiterungsfunktion erstellen
Wie Sie im folgenden Code sehen können, haben wir wie gewohnt eine Funktion der obersten Ebene definiert, um eine Erweiterungsfunktion zu deklarieren. Diese Erweiterungsfunktion befindet sich in einem Paket namens com.chike.kotlin.strings
.
Um eine Erweiterungsfunktion zu erstellen, müssen Sie den Namen der Klasse, die Sie erweitern, vor den Funktionsnamen stellen. Der Klassenname oder der Typ, für den die Erweiterung definiert ist, wird als Empfängertyp bezeichnet, und das Empfängerobjekt ist die Klasseninstanz oder der Wert, für den die Erweiterungsfunktion aufgerufen wird.
1 |
package com.chike.kotlin.strings |
2 |
|
3 |
fun String.upperCaseFirstLetter(): String { |
4 |
return this.substring(0, 1).toUpperCase().plus(this.substring(1)) |
5 |
}
|
Beachten Sie, dass this
Schlüsselwort this im Funktionskörper auf das Empfängerobjekt oder die Instanz verweist.
Aufrufen einer Erweiterungsfunktion
Nach dem Erstellen Ihrer Erweiterungsfunktion müssen Sie die Erweiterungsfunktion zunächst in andere Pakete oder Dateien importieren, die in dieser Datei oder diesem Paket verwendet werden sollen. Das Aufrufen der Funktion entspricht dann dem Aufrufen einer anderen Methode der Empfängertypklasse.
1 |
package com.chike.kotlin.packagex |
2 |
|
3 |
import com.chike.kotlin.strings.upperCaseFirstLetter |
4 |
|
5 |
print("chike".upperCaseFirstLetter()) // "Chike" |
Im obigen Beispiel ist der Empfängertyp die String
-Klasse und das Empfängerobjekt ist "chike"
. Wenn Sie eine IDE wie IntelliJ IDEA verwenden, die über die IntelliSense-Funktion verfügt, wird Ihre neue Erweiterungsfunktion in der Liste der anderen Funktionen in einem String
-Typ vorgeschlagen.



Java-Interoperabilität
Beachten Sie, dass Kotlin hinter den Kulissen eine statische Methode erstellt. Das erste Argument dieser statischen Methode ist das Empfängerobjekt. Daher ist es für Java-Aufrufer einfach, diese statische Methode aufzurufen und dann das Empfängerobjekt als Argument zu übergeben.
Wenn unsere Erweiterungsfunktion beispielsweise in einer StringUtils.kt-Datei deklariert wurde, erstellt der Kotlin-Compiler eine Java-Klasse StringUtilsKt
mit einer statischen Methode UpperCaseFirstLetter()
.
1 |
/* Java */
|
2 |
package com.chike.kotlin.strings |
3 |
|
4 |
public class StringUtilsKt { |
5 |
|
6 |
public static String upperCaseFirstLetter(String str) { |
7 |
return str.substring(0, 1).toUpperCase() + str.substring(1); |
8 |
}
|
9 |
}
|
Dies bedeutet, dass Java-Aufrufer die Methode wie bei jeder anderen statischen Methode einfach aufrufen können, indem sie auf ihre generierte Klasse verweisen.
1 |
/* Java */
|
2 |
print(StringUtilsKt.upperCaseFirstLetter("chike")); // "Chike" |
Denken Sie daran, dass dieser Java-Interop-Mechanismus der Funktionsweise von Funktionen der obersten Ebene in Kotlin ähnelt, wie wir im Beitrag Mehr Spaß mit Funktionen beschrieben haben!
Erweiterungsfunktionen vs. Mitgliedsfunktionen
Beachten Sie, dass Erweiterungsfunktionen Funktionen, die bereits in einer Klasse oder Schnittstelle deklariert sind, nicht überschreiben können - sogenannte Elementfunktionen (wenn Sie eine Auffrischung der Elementfunktionen in Kotlin wünschen, lesen Sie das vorherige Tutorial in dieser Reihe). Wenn Sie also eine Erweiterungsfunktion mit genau derselben Funktionssignatur definiert haben - denselben Funktionsnamen und dieselbe Anzahl, denselben Typ und dieselbe Reihenfolge von Argumenten, unabhängig vom Rückgabetyp -, wird sie vom Kotlin-Compiler nicht aufgerufen. Beim Kompilieren sucht der Kotlin-Compiler beim Aufrufen einer Funktion zunächst nach einer Übereinstimmung in den im Instanztyp oder in seinen Oberklassen definierten Elementfunktionen. Wenn es eine Übereinstimmung gibt, wird diese Mitgliedsfunktion aufgerufen oder gebunden. Wenn keine Übereinstimmung vorliegt, ruft der Compiler eine Erweiterungsfunktion dieses Typs auf.
Zusammengefasst also: Mitgliedsfunktionen gewinnen immer.
Sehen wir uns ein praktisches Beispiel an.
1 |
class Student { |
2 |
|
3 |
fun printResult() { |
4 |
println("Printing student result") |
5 |
}
|
6 |
|
7 |
fun expel() { |
8 |
println("Expelling student from school") |
9 |
}
|
10 |
}
|
11 |
|
12 |
fun Student.printResult() { |
13 |
println("Extension function printResult()") |
14 |
}
|
15 |
|
16 |
fun Student.expel(reason: String) { |
17 |
println("Expelling student from School. Reason: \"$reason\"") |
18 |
}
|
Im obigen Code haben wir einen Typ namens Student
mit zwei Elementfunktionen definiert: printResult()
und expel()
. Wir haben dann zwei Erweiterungsfunktionen definiert, die dieselben Namen wie die Elementfunktionen haben.
Rufen wir die Funktion printResult()
auf und sehen das Ergebnis.
1 |
val student = Student() |
2 |
student.printResult() // Printing student result |
Wie Sie sehen können, war die aufgerufene oder gebundene Funktion die Elementfunktion und nicht die Erweiterungsfunktion mit derselben Funktionssignatur (obwohl IntelliJ IDEA Ihnen dennoch einen Hinweis dazu geben würde).
Der Aufruf der Mitgliedsfunktion expel()
und der Erweiterungsfunktion expel(reason: String)
führt jedoch zu unterschiedlichen Ergebnissen, da die Funktionssignaturen unterschiedlich sind.
1 |
student.expel() // Expelling student from school |
2 |
student.expel("stole money") // Expelling student from School. Reason: "stole money" |
Funktionen zur Erweiterung von Mitgliedern
Sie werden eine Erweiterungsfunktion die meiste Zeit als Funktion der obersten Ebene deklarieren. Beachten Sie jedoch, dass Sie sie auch als Elementfunktionen deklarieren können.
1 |
class ClassB { |
2 |
|
3 |
}
|
4 |
|
5 |
class ClassA { |
6 |
|
7 |
fun ClassB.exFunction() { |
8 |
print(toString()) // calls ClassB toString() |
9 |
}
|
10 |
|
11 |
fun callExFunction(classB: ClassB) { |
12 |
classB.exFunction() // call the extension function |
13 |
}
|
14 |
}
|
Im obigen Code haben wir eine Erweiterungsfunktion exFunction()
vom Typ ClassB
in einer anderen Klasse ClassA
deklariert. Der Versandempfänger ist die Instanz der Klasse, in der die Erweiterung deklariert ist, und die Instanz des Empfängertyps der Erweiterungsmethode wird als Erweiterungsempfänger bezeichnet. Beachten Sie, dass der Compiler den Nebenstellenempfänger auswählt, wenn zwischen dem Versandempfänger und dem Nebenstellenempfänger ein Namenskonflikt oder eine Schattenbildung auftritt.
Im obigen Codebeispiel ist der Erweiterungsempfänger eine Instanz von ClassB
. Dies bedeutet, dass die toString()
-Methode vom Typ ClassB
ist, wenn sie innerhalb der Erweiterungsfunktion exFunction()
aufgerufen wird. Damit wir stattdessen die toString()
-Methode des Versandempfängers ClassA
aufrufen können, müssen wir ein qualifiziertes this
verwenden:
1 |
// ...
|
2 |
fun ClassB.extFunction() { |
3 |
print(this@ClassA.toString()) // now calls ClassA toString() method |
4 |
}
|
5 |
// ...
|
2. Funktionen höherer Ordnung
Eine Funktion höherer Ordnung ist nur eine Funktion, die eine andere Funktion (oder einen Lambda-Ausdruck) als Parameter verwendet, eine Funktion zurückgibt oder beides ausführt. Die Auflistungsfunktion last()
ist ein Beispiel für eine Funktion höherer Ordnung aus der Standardbibliothek.
1 |
val stringList: List<String> = listOf("in", "the", "club") |
2 |
print(stringList.last{ it.length == 3}) // "the" |
Hier haben wir ein Lambda an die last
-Funktion übergeben, die als Prädikat für die Suche innerhalb einer Teilmenge von Elementen dient. Wir werden uns nun mit der Erstellung eigener Funktionen höherer Ordnung in Kotlin befassen.
Erstellen einer Funktion höherer Ordnung
Wenn Sie sich die Funktion circleOperation()
unten ansehen, hat sie zwei Parameter. Der erste, radius
, akzeptiert ein Double, und der zweite, op
, ist eine Funktion, die ein Double als Eingabe akzeptiert und auch ein Double als Ausgabe zurückgibt - wir können prägnanter sagen, dass der zweite Parameter "eine Funktion von Double zu Double" ist. .
Beachten Sie, dass die Parametertypen der op
-Funktion für die Funktion in Klammern ()
eingeschlossen sind und der Ausgabetyp durch einen Pfeil getrennt ist. Die Funktion circleOperation()
ist ein typisches Beispiel für eine Funktion höherer Ordnung, die eine Funktion als Parameter akzeptiert.
1 |
fun calCircumference(radius: Double) = (2 * Math.PI) * radius |
2 |
|
3 |
fun calArea(radius: Double): Double = (Math.PI) * Math.pow(radius, 2.0) |
4 |
|
5 |
fun circleOperation(radius: Double, op: (Double) -> Double): Double { |
6 |
val result = op(radius) |
7 |
return result |
8 |
}
|
Aufrufen einer Funktion höherer Ordnung
Beim Aufruf dieser Funktion circleOperation()
übergeben wir ihr eine weitere Funktion, calArea()
. (Beachten Sie, dass der Funktionsaufruf nicht kompiliert wird, wenn die Methodensignatur der übergebenen Funktion nicht mit der deklariert, die die Funktion höherer Ordnung deklariert.)
Um die Funktion calArea()
als Parameter an circleOperation()
zu übergeben, müssen wir ::
voranstellen und die Klammern ()
weglassen.
1 |
print(circleOperation(3.0, ::calArea)) // 28.274333882308138 |
2 |
print(circleOperation(3.0, calArea)) // won't compile |
3 |
print(circleOperation(3.0, calArea())) // won't compile |
4 |
print(circleOperation(6.7, ::calCircumference)) // 42.09734155810323 |
Wenn Sie Funktionen höherer Ordnung mit Bedacht einsetzen, kann unser Code leichter lesbar und verständlicher werden.
Lambdas und Funktionen höherer Ordnung
Wir können ein Lambda (oder Funktionsliteral) auch direkt an eine Funktion höherer Ordnung übergeben, wenn wir die Funktion aufrufen:
1 |
circleOperation(5.3, { (2 * Math.PI) * it }) |
Denken Sie daran, damit wir vermeiden, das Argument explizit zu benennen, können wir den für uns automatisch generierten it
-Argumentnamen nur verwenden, wenn das Lambda ein Argument hat. (Wenn Sie eine Auffrischung zu Lambda in Kotlin wünschen, besuchen Sie das Tutorial Mehr Spaß mit Funktionen in dieser Reihe.)
Rückgabe einer Funktion
Denken Sie daran, dass Funktionen höherer Ordnung nicht nur eine Funktion als Parameter akzeptieren, sondern auch eine Funktion an Aufrufer zurückgeben können.
1 |
fun multiplier(factor: Double): (Double) -> Double = { number -> number*factor } |
Hier gibt die Funktion multiplier()
eine Funktion zurück, die den angegebenen Faktor auf eine beliebige übergebene Zahl anwendet. Diese zurückgegebene Funktion ist ein Lambda (oder Funktionsliteral) von double bis double (was bedeutet, dass der Eingabeparameter der zurückgegebenen Funktion ein doppelter Typ ist und das Ausgabeergebnis auch ein doppelter Typ ist).
1 |
val doubler = multiplier(2) |
2 |
print(doubler(5.6)) // 11.2 |
Um dies zu testen, haben wir den Faktor zwei übergeben und die zurückgegebene Funktion dem Variablenverdoppler zugewiesen. Wir können dies wie eine normale Funktion aufrufen, und jeder Wert, den wir übergeben, wird verdoppelt.
3. Verschlüsse
Ein Abschluss ist eine Funktion, die Zugriff auf Variablen und Parameter hat, die in einem äußeren Bereich definiert sind.
1 |
fun printFilteredNamesByLength(length: Int) { |
2 |
val names = arrayListOf("Adam", "Andrew", "Chike", "Kechi") |
3 |
val filterResult = names.filter { |
4 |
it.length == length |
5 |
}
|
6 |
println(filterResult) |
7 |
}
|
8 |
|
9 |
printFilteredNamesByLength(5) // [Chike, Kechi] |
Im obigen Code verwendet das an die Sammlungsfunktion filter()
übergebene Lambda den Parameter length
der äußeren Funktion printFilteredNamesByLength()
. Beachten Sie, dass dieser Parameter außerhalb des Bereichs des Lambda definiert ist, das Lambda jedoch weiterhin auf die length
zugreifen kann. Dieser Mechanismus ist ein Beispiel für das Schließen in der funktionalen Programmierung.
4. Inline-Funktionen
In Mehr Spaß mit Funktionen erwähnte ich, dass der Kotlin-Compiler in früheren Java-Versionen hinter den Kulissen beim Erstellen von Lambda-Ausdrücken eine anonyme Klasse erstellt.
Leider führt dieser Mechanismus zu Overhead, da jedes Mal, wenn wir ein Lambda erstellen, eine anonyme Klasse unter der Haube erstellt wird. Außerdem fügt ein Lambda, das den äußeren Funktionsparameter oder die lokale Variable mit einem Abschluss verwendet, seinen eigenen Speicherzuweisungsaufwand hinzu, da dem Heap bei jedem Aufruf ein neues Objekt zugewiesen wird.
Vergleichen von Inline-Funktionen mit normalen Funktionen
Um diesen Aufwand zu vermeiden, hat uns das Kotlin-Team den inline
-Modifikator für Funktionen zur Verfügung gestellt. Eine Funktion höherer Ordnung mit dem inline
-Modifikator wird während der Codekompilierung eingefügt. Mit anderen Worten, der Compiler kopiert das Lambda (oder Funktionsliteral) sowie den Funktionskörper höherer Ordnung und fügt sie an der Aufrufstelle ein.
Schauen wir uns ein praktisches Beispiel an.
1 |
fun circleOperation(radius: Double, op: (Double) -> Double) { |
2 |
println("Radius is $radius") |
3 |
val result = op(radius) |
4 |
println("The result is $result") |
5 |
}
|
6 |
|
7 |
fun main(args: Array<String>) { |
8 |
circleOperation(5.3, { (2 * Math.PI) * it }) |
9 |
}
|
Im obigen Code haben wir eine Funktion circleOperation()
höherer Ordnung, die den inline
-Modifikator nicht hat. Lassen Sie uns nun den Kotlin-Bytecode sehen, der beim Kompilieren und Dekompilieren des Codes generiert wird, und ihn dann mit einem Code vergleichen, der den inline
-Modifikator enthält.
1 |
public final class InlineFunctionKt { |
2 |
public static final void circleOperation(double radius, @NotNull Function1 op) { |
3 |
Intrinsics.checkParameterIsNotNull(op, "op"); |
4 |
String var3 = "Radius is " + radius; |
5 |
System.out.println(var3); |
6 |
double result = ((Number)op.invoke(radius)).doubleValue(); |
7 |
String var5 = "The result is " + result; |
8 |
System.out.println(var5); |
9 |
}
|
10 |
|
11 |
public static final void main(@NotNull String[] args) { |
12 |
Intrinsics.checkParameterIsNotNull(args, "args"); |
13 |
circleOperation(5.3D, (Function1)null.INSTANCE); |
14 |
}
|
15 |
}
|
Im obigen generierten Java-Bytecode sehen Sie, dass der Compiler die Funktion circleOperation()
innerhalb der main()
-Methode aufgerufen hat.
Geben wir nun stattdessen die Funktion höherer Ordnung als inline
an und sehen auch den generierten Bytecode.
1 |
inline fun circleOperation(radius: Double, op: (Double) -> Double) { |
2 |
println("Radius is $radius") |
3 |
val result = op(radius) |
4 |
println("The result is $result") |
5 |
}
|
6 |
|
7 |
fun main(args: Array<String>) { |
8 |
circleOperation(5.3, { (2 * Math.PI) * it }) |
9 |
}
|
Um eine Funktion höherer Ordnung inline zu machen, müssen wir den inline
-Modifikator vor dem Schlüsselwort fun
einfügen, genau wie im obigen Code. Überprüfen wir auch den für diese Inline-Funktion generierten Bytecode.
1 |
public static final void circleOperation(double radius, @NotNull Function1 op) { |
2 |
Intrinsics.checkParameterIsNotNull(op, "op"); |
3 |
String var4 = "Radius is " + radius; |
4 |
System.out.println(var4); |
5 |
double result = ((Number)op.invoke(radius)).doubleValue(); |
6 |
String var6 = "The result is " + result; |
7 |
System.out.println(var6); |
8 |
}
|
9 |
|
10 |
public static final void main(@NotNull String[] args) { |
11 |
Intrinsics.checkParameterIsNotNull(args, "args"); |
12 |
double radius$iv = 5.3D; |
13 |
String var3 = "Radius is " + radius$iv; |
14 |
System.out.println(var3); |
15 |
double result$iv = 6.283185307179586D * radius$iv; |
16 |
String var9 = "The result is " + result$iv; |
17 |
System.out.println(var9); |
18 |
}
|
Wenn Sie sich den generierten Bytecode für die Inline-Funktion innerhalb der main()
-Funktion ansehen, können Sie feststellen, dass sie statt des Aufrufs der circleOperation()
-Funktion nun den circleOperation()
-Funktionskörper einschließlich des Lambda-Bodys kopiert und an ihrer Aufrufstelle eingefügt hat.
Mit diesem Mechanismus wurde unser Code erheblich optimiert - keine anonymen Klassen mehr erstellt oder zusätzliche Speicherzuweisungen vorgenommen. Aber seien Sie sich sehr bewusst, dass wir hinter den Kulissen einen größeren Bytecode haben würden als zuvor. Aus diesem Grund wird dringend empfohlen, nur kleinere Funktionen höherer Ordnung zu integrieren, die Lambda als Parameter akzeptieren.
Viele der Funktionen höherer Ordnung der Standardbibliothek in Kotlin verfügen über den Inline-Modifikator. Wenn Sie beispielsweise einen Blick auf die Funktionen der Erfassungsoperation filter()
und first()
werfen, werden Sie feststellen, dass sie über den inline
-Modifikator verfügen und auch klein sind.
1 |
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> { |
2 |
return filterTo(ArrayList<T>(), predicate) |
3 |
}
|
4 |
|
5 |
public inline fun <T> Iterable<T>.first(predicate: (T) -> Boolean): T { |
6 |
for (element in this) if (predicate(element)) return element |
7 |
throw NoSuchElementException("Collection contains no element matching the predicate.") |
8 |
}
|
Denken Sie daran, keine normalen Funktionen zu integrieren, die kein Lambda als Parameter akzeptieren! Sie werden kompiliert, aber es würde keine signifikante Leistungsverbesserung geben (IntelliJ IDEA würde sogar einen Hinweis darauf geben).
Der noinline
-Modifikator
Wenn eine Funktion mehr als zwei Lambda-Parameter enthält, können Sie mithilfe des Modifikators noinline
für den Parameter entscheiden, welches Lambda nicht inline geschaltet werden soll. Diese Funktionalität ist besonders nützlich für einen Lambda-Parameter, der viel Code aufnimmt. Mit anderen Worten, der Kotlin-Compiler kopiert und fügt das Lambda, in dem es aufgerufen wird, nicht ein und erstellt stattdessen eine anonyme Klasse hinter den Kulissen.
1 |
inline fun myFunc(op: (Double) -> Double, noinline op2: (Int) -> Int) { |
2 |
// perform operations
|
3 |
}
|
Hier haben wir den noinline
-Modifikator in den zweiten Lambda-Parameter eingefügt. Beachten Sie, dass dieser Modifikator nur gültig ist, wenn die Funktion über den inline
-Modifikator verfügt.
Stapelverfolgung in Inline-Funktionen
Beachten Sie, dass sich der Methodenaufrufstapel in der Stapelverfolgung von einer normalen Funktion ohne den Inline-Modifikator unterscheidet, wenn eine Ausnahme innerhalb einer inline
-Funktion ausgelöst wird. Dies liegt an dem Kopier- und Einfügemechanismus, den der Compiler für Inline-Funktionen verwendet. Das Coole ist, dass IntelliJ IDEA uns hilft, den Methodenaufruf-Stack im Stack-Trace für eine Inline-Funktion einfach zu navigieren. Sehen wir uns ein Beispiel an.
1 |
inline fun myFunc(op: (Double) -> Double) { |
2 |
throw Exception("message 123") |
3 |
}
|
4 |
|
5 |
fun main(args: Array<String>) { |
6 |
myFunc({ 4.5 }) |
7 |
}
|
Im obigen Code wird absichtlich eine Ausnahme in der Inline-Funktion myFunc()
ausgelöst. Lassen Sie uns nun den Stack-Trace in IntelliJ IDEA sehen, wenn der Code ausgeführt wird. Wenn Sie sich den Screenshot unten ansehen, sehen Sie, dass wir zwei Navigationsoptionen zur Auswahl haben: den Inline-Funktionskörper oder die Inline-Funktionsaufrufseite. Wenn Sie Ersteres auswählen, gelangen Sie zu dem Punkt, an dem die Ausnahme im Funktionskörper ausgelöst wurde, während Letzteres uns zu dem Punkt führt, an dem die Methode aufgerufen wurde.



Wenn die Funktion keine Inline-Funktion wäre, würde unsere Stapelverfolgung derjenigen entsprechen, mit der Sie möglicherweise bereits vertraut sind:



Schlussfolgerung
In diesem Tutorial haben Sie noch mehr gelernt, was Sie mit Funktionen in Kotlin tun können. Wir haben Folgendes behandelt:
- Erweiterungsfunktionen
- Funktionen höherer Ordnung
- Verschlüsse
- Inline-Funktionen
Im nächsten Tutorial der Kotlin From Scratch-Reihe werden wir uns mit objektorientierter Programmierung befassen und lernen, wie Klassen in Kotlin funktionieren. Bis bald!
Um mehr über die Kotlin-Sprache zu erfahren, empfehle ich, die Kotlin-Dokumentation zu besuchen. Oder sehen Sie sich einige unserer anderen Beiträge zur Entwicklung von Android-Apps hier auf Envato Tuts+ an!
- Android SDKJava vs. Kotlin: Sollten Sie Kotlin für die Android-Entwicklung verwenden?Jessica Thornsby
- Android SDKEinführung in Android-ArchitekturkomponentenZinn Megali
- Android SDKWie kann man die Google Cloud Vision-API in Android-Apps verwenden?Ashraff Hathibelagal
- Android SDKWas sind Android Instant Apps?Jessica Thornsby