Die Programmiersprache Kotlin kennenlernen

Kotlin ist eine Programmiersprache. die weltweit von Android-Entwicklern weit verbreitet sind. Dieses Thema dient als Kotlin-Code Crashkurs für einen schnellen Einstieg.

Variablendeklaration

In Kotlin werden zum Deklarieren von Variablen zwei verschiedene Keywords verwendet: val und var.

  • Verwenden Sie val für eine Variable, deren Wert sich nie ändert. Sie können keinen Wert neu zuweisen in eine Variable übergeben, die mit val deklariert wurde.
  • Verwenden Sie var für eine Variable, deren Wert sich ändern kann.

Im folgenden Beispiel ist count eine Variable vom Typ Int, der ein Anfangswert von 10:

var count: Int = 10

Int ist ein Typ, der eine Ganzzahl darstellt, einer von vielen numerischen Typen, in Kotlin dargestellt werden können. Ähnlich wie bei anderen Sprachen können Sie auch Byte, Short, Long, Float und Double abhängig von Ihren numerischen Daten.

Das Keyword var bedeutet, dass Sie count Werte nach Bedarf neu zuweisen können. Für Sie können beispielsweise den Wert von count von 10 in 15 ändern:

var count: Int = 10
count = 15

Einige Werte sollen jedoch nicht geändert werden. Beispiel: String mit dem Namen languageName. Wenn languageName immer einen Wert enthalten soll "Kotlin" enthält, können Sie languageName mit dem Schlüsselwort val deklarieren:

val languageName: String = "Kotlin"

Mit diesen Keywords können Sie genau angeben, was geändert werden kann. Sie können sie für Folgendes nutzen: Ihren Vorteil je nach Bedarf. Wenn ein Variablenverweis neu zuweisbar sein muss, deklarieren Sie es als var. Andernfalls verwenden Sie val.

Typinferenz

In Anlehnung an das vorherige Beispiel werden Sie beim Zuweisen eines Anfangswerts languageName, kann der Kotlin-Compiler den Typ basierend auf dem Typ den zugewiesenen Wert.

Da der Wert von "Kotlin" vom Typ String ist, leitet der Compiler Folgendes ab: languageName ist auch ein String. Kotlin ist ein statisch typisierter Sprache. Das bedeutet, dass der Typ zum Kompilierungszeitpunkt aufgelöst wird und nie Änderungen.

Im folgenden Beispiel wird languageName als String abgeleitet. Sie können also alle Funktionen aufrufen, die nicht zur Klasse String gehören:

val languageName = "Kotlin"
val upperCaseName = languageName.toUpperCase()

// Fails to compile
languageName.inc()

toUpperCase() ist eine Funktion, die nur für Variablen des Typs String. Da der Kotlin-Compiler languageName als String abgeleitet hat, können Sie sicher toUpperCase() anrufen. inc() ist jedoch ein Int-Operator daher kann sie nicht für String aufgerufen werden. Der Typ von Kotlin-Typ Ableitung ermöglicht Ihnen Präzision und Typsicherheit.

Nullsicherheit

In einigen Sprachen kann eine Referenztypvariable deklariert werden, ohne einen expliziten Anfangswert. In diesen Fällen enthalten die Variablen normalerweise einen NULL-Wert Wert. Kotlin-Variablen dürfen standardmäßig keine Nullwerte enthalten. Das bedeutet, dass der folgendes Snippet ist ungültig:

// Fails to compile
val languageName: String = null

Damit eine Variable einen Nullwert enthalten kann, muss sie vom Typ Nullwerte zulässig sein. Sie können Sie können eine Variable als Nullwerte angeben, indem Sie ihrem Typ als Suffix ? hinzufügen, wie hier gezeigt im folgenden Beispiel:

val languageName: String? = null

Bei einem String?-Typ können Sie entweder einen String-Wert oder einen null-Wert languageName.

Sie müssen mit Variablen, für die Nullwerte zulässig sind, vorsichtig umgehen, da sonst ein gefürchtetes Risiko besteht. NullPointerException Wenn Sie in Java beispielsweise versuchen, eine Methode bei einem Nullwert ab, stürzt das Programm ab.

Kotlin bietet eine Reihe von Mechanismen für die sichere Arbeit mit Nullwerten Variablen. Weitere Informationen finden Sie unter Gängige Kotlin-Muster in Android: Null-Zulässigkeit

Bedingungen

Kotlin bietet mehrere Mechanismen zum Implementieren bedingter Logik. Die meisten Dazu gehört häufig eine if-else-Anweisung. Wenn ein Ausdruck in Klammern neben einem if-Keyword wird als true ausgewertet, dann wird der Code in diesem Zweig (d.h. der direkt nachfolgende Code, der in geschweifte Klammern gesetzt ist) geschweifte Klammern) wird ausgeführt. Andernfalls wird der Code im Zweig else ausgeführt.

if (count == 42) {
    println("I have the answer.")
} else {
    println("The answer eludes me.")
}

Sie können mehrere Bedingungen mit else if darstellen. Auf diese Weise können Sie eine detailliertere, komplexe Logik innerhalb einer einzelnen bedingten Anweisung, wie in im folgenden Beispiel:

if (count == 42) {
    println("I have the answer.")
} else if (count > 35) {
    println("The answer is close.")
} else {
    println("The answer eludes me.")
}

Bedingte Anweisungen sind nützlich, um zustandsorientierte Logik darzustellen. Sie können dass Sie sich beim Schreiben wiederholen. Im obigen Beispiel geben Sie einfach in jedem Zweig ein String aus. Um diese Wiederholung zu vermeiden, bietet Kotlin bedingten Ausdrücken. Das letzte Beispiel kann so umgeschrieben werden:

val answerString: String = if (count == 42) {
    "I have the answer."
} else if (count > 35) {
    "The answer is close."
} else {
    "The answer eludes me."
}

println(answerString)

Implizit gibt jeder bedingte Zweig das Ergebnis des Ausdrucks auf seiner in der letzten Zeile, sodass Sie kein return-Keyword verwenden müssen. Da das Ergebnis alle drei Zweige vom Typ String sind, lautet das Ergebnis des if-else-Ausdrucks: ebenfalls vom Typ String. In diesem Beispiel ist answerString ein Anfangsbuchstabe zugewiesen Wert aus dem Ergebnis des if-else-Ausdrucks. Typinferenz kann verwendet werden, um die explizite Typdeklaration für answerString wegzulassen, aber das ist oft um sie zur Verdeutlichung aufzunehmen.

Wenn die Komplexität Ihrer bedingten Anweisung zunimmt, sollten Sie Ersetzen Sie Ihren if-else-Ausdruck durch einen when-Ausdruck, wie in den folgendes Beispiel:

val answerString = when {
    count == 42 -> "I have the answer."
    count > 35 -> "The answer is close."
    else -> "The answer eludes me."
}

println(answerString)

Jeder Zweig in einem when-Ausdruck wird durch eine Bedingung, einen Pfeil (->) und ein Ergebnis. Wenn die Bedingung auf der linken Seite des Pfeils als wahr ausgewertet, dann ist das Ergebnis des Ausdrucks auf der rechten Seite zurückgegeben. Beachten Sie, dass die Ausführung nicht von einem Zweig zum nächsten erfolgt. Der Code im Beispiel für den when-Ausdruck entspricht funktional dem Code in aus dem vorherigen Beispiel, ist aber wohl leichter zu lesen.

Die Bedingungen von Kotlin heben eines der leistungsstärkeren Funktionen hervor, intelligentes Streamen. Anstatt den Operator „safe-call“ oder den Operator „not-null“ zu verwenden um mit Werten zu arbeiten, für die Nullwerte zulässig sind, können Sie stattdessen prüfen, Variable enthält einen Verweis auf einen Nullwert mithilfe einer bedingten Anweisung, wie Beispiel:

val languageName: String? = null
if (languageName != null) {
    // No need to write languageName?.toUpperCase()
    println(languageName.toUpperCase())
}

Innerhalb des bedingten Zweigs kann languageName so behandelt werden, als ob keine Nullwerte zulässig sind. Kotlin ist so intelligent, dass sie erkennt, dass die Bedingung für die Ausführung des Branch ist, dass languageName keinen Nullwert enthält. Sie müssen also languageName in diesem Zweig als Nullwerte zulässig. Diese intelligente Übertragung funktioniert für null Überprüfungen, Typprüfungen, oder eine Bedingung erfüllen, die ein Vertrag.

Funktionen

Sie können einen oder mehrere Ausdrücke in einer Funktion gruppieren. Anstatt die App zu wiederholen jedes Mal, wenn Sie ein Ergebnis benötigen, die gleiche Reihe von Ausdrücken enthält, die Ausdrücke in einer Funktion ansehen und stattdessen diese Funktion aufrufen.

Um eine Funktion zu deklarieren, verwenden Sie das Schlüsselwort fun, gefolgt vom Funktionsnamen. Definieren Sie als Nächstes die Eingabetypen, falls zutreffend, und deklarieren Sie die Art der zurückgegebenen Ausgabe. Im Text einer Funktion definieren Sie Ausdrücke, die beim Aufrufen Ihrer Funktion aufgerufen werden.

Aufbauend auf den vorherigen Beispielen finden Sie hier eine vollständige Kotlin-Funktion:

fun generateAnswerString(): String {
    val answerString = if (count == 42) {
        "I have the answer."
    } else {
        "The answer eludes me"
    }

    return answerString
}

Die Funktion im obigen Beispiel hat den Namen generateAnswerString. Es nimmt keine Eingaben vor. Es wird ein Ergebnis vom Typ String ausgegeben. So rufen Sie eine -Funktion ihren Namen, gefolgt vom Aufrufoperator (()) ein. Im Im Beispiel unten wird die Variable answerString mit dem Ergebnis aus generateAnswerString()

val answerString = generateAnswerString()

Funktionen können Argumente als Eingabe annehmen, wie im folgenden Beispiel gezeigt:

fun generateAnswerString(countThreshold: Int): String {
    val answerString = if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }

    return answerString
}

Beim Deklarieren einer Funktion können Sie eine beliebige Anzahl von Argumenten und deren Typen. Im obigen Beispiel verwendet generateAnswerString() ein Argument mit dem Namen countThreshold vom Typ Int. Innerhalb der Funktion können Sie auf das Argument mithilfe seines Namens.

Wenn Sie diese Funktion aufrufen, müssen Sie in der Funktion ein Argument angeben. in Klammern:

val answerString = generateAnswerString(42)

Vereinfachung von Funktionsdeklarationen

generateAnswerString() ist eine ziemlich einfache Funktion. Mit der Funktion wird ein und kehrt sofort zurück. Wenn das Ergebnis eines einzelnen Ausdrucks zurückgegeben wird, können Sie die Deklaration einer lokalen Variable überspringen, gibt das Ergebnis des in der Funktion enthaltenen if-else-Ausdrucks zurück: Beispiel:

fun generateAnswerString(countThreshold: Int): String {
    return if (count > countThreshold) {
        "I have the answer."
    } else {
        "The answer eludes me."
    }
}

Sie können das Rückgabe-Keyword auch durch den Zuweisungsoperator ersetzen:

fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
        "I have the answer"
    } else {
        "The answer eludes me"
    }

Anonyme Funktionen

Nicht jede Funktion benötigt einen Namen. Einige Funktionen lassen sich ihre Ein- und Ausgaben. Diese Funktionen werden als anonyme Funktionen bezeichnet. Ich einen Verweis auf eine anonyme Funktion beibehalten, indem dieser Verweis auf später die anonyme Funktion aufrufen. Du kannst die Referenz auch wie bei anderen Referenztypen.

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

Wie benannte Funktionen können auch anonyme Funktionen eine beliebige Anzahl von Ausdrücken enthalten. Der von der Funktion zurückgegebene Wert ist das Ergebnis des endgültigen Ausdrucks.

Im obigen Beispiel enthält stringLengthFunc einen Verweis auf eine anonyme -Funktion, die eine String als Eingabe verwendet und die Länge der Eingabe zurückgibt String als Ausgabe vom Typ Int. Daher lautet der Typ der Funktion gekennzeichnet als (String) -> Int. Dieser Code ruft die Funktion jedoch nicht auf. Um das Ergebnis der Funktion abzurufen, müssen Sie sie wie mit einem benannte Funktion ein. Sie müssen beim Aufrufen von stringLengthFunc eine String angeben, wie Beispiel:

val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

val stringLength: Int = stringLengthFunc("Android")

Höhere Funktionen

Eine Funktion kann eine andere Funktion als Argument annehmen. Funktionen, die andere Funktionen als Argumente werden als Funktionen höherer Ordnung bezeichnet. Dieses Muster ist nützlich für die Kommunikation zwischen Komponenten, so wie Sie es auch mit einem Callback-Schnittstelle in Java verwenden.

Hier ist ein Beispiel für eine übergeordnete Funktion:

fun stringMapper(str: String, mapper: (String) -> Int): Int {
    // Invoke function
    return mapper(str)
}

Die stringMapper()-Funktion übernimmt eine String zusammen mit einer Funktion, die leitet einen Int-Wert aus einer String ab, die Sie ihm übergeben.

Sie können stringMapper() aufrufen, indem Sie ein String und eine Funktion übergeben, den anderen Eingabeparameter erfüllt, nämlich eine Funktion, die ein String als ein Int ein- und ausgibt, wie im folgenden Beispiel gezeigt:

stringMapper("Android", { input ->
    input.length
})

Wenn die anonyme Funktion der letzte Parameter ist, der für eine Funktion definiert ist, können Sie außerhalb der zum Aufrufen der Funktion verwendeten Klammern übergeben, wie in der folgendes Beispiel:

stringMapper("Android") { input ->
    input.length
}

Anonyme Funktionen sind in der gesamten Kotlin-Standardbibliothek zu finden. Für finden Sie unter Funktionen höherer Ordnung und Lambdas:

Klassen

Alle bisher erwähnten Typen sind in die Kotlin-Programmierung integriert. Sprache. Wenn Sie einen eigenen benutzerdefinierten Typ hinzufügen möchten, können Sie eine Klasse definieren mit dem Schlüsselwort class, wie im folgenden Beispiel gezeigt:

class Car

Properties

Klassen stellen den Zustand mithilfe von Eigenschaften dar. A property ist Eine Variable auf Klassenebene, die einen Getter, einen Setter und ein unterstützendes Feld enthalten kann. Da ein Auto zum Anfahren Räder benötigt, können Sie eine Liste mit Wheel-Objekten als von Car, wie im folgenden Beispiel gezeigt:

class Car {
    val wheels = listOf<Wheel>()
}

wheels ist eine public val, d. h., wheels kann über außerhalb des Car-Kurses und kann nicht neu zugewiesen werden. Wenn Sie eine Instanz von Car müssen Sie zuerst dessen Konstruktor aufrufen. Hier können Sie auf ihre barrierefreien Properties zuzugreifen.

val car = Car() // construct a Car
val wheels = car.wheels // retrieve the wheels value from the Car

Wenn Sie Ihre Räder anpassen möchten, können Sie einen benutzerdefinierten Konstruktor definieren, der gibt an, wie Ihre Klassenattribute initialisiert werden:

class Car(val wheels: List<Wheel>)

Im obigen Beispiel übernimmt der Klassenkonstruktor ein List<Wheel> als -Konstruktor und verwendet dieses Argument, um sein wheels-Element zu initialisieren Property.

Klassenfunktionen und Kapselung

Klassen verwenden Funktionen, um das Verhalten zu modellieren. Funktionen können den Status ändern um nur die Daten sichtbar zu machen, die Sie bereitstellen möchten. Diese Zugriffssteuerung gehört zu ein größeres objektorientiertes Konzept, die Datenkapselung.

Im folgenden Beispiel wird das Attribut doorLock vor allem privat behandelt. außerhalb der Car-Klasse. Wenn du das Auto entriegeln möchtest, musst du die unlockDoor() aufrufen -Funktion übergeben einen gültigen Schlüssel, wie im folgenden Beispiel gezeigt:

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

Wenn Sie anpassen möchten, wie auf eine Property verwiesen wird, können Sie eine einen benutzerdefinierten Getter und Setter. Wenn Sie beispielsweise das Attribut und den Zugriff auf seinen Setter einschränken, können Sie diesen Setter als private:

class Car(val wheels: List<Wheel>) {

    private val doorLock: DoorLock = ...

    var gallonsOfFuelInTank: Int = 15
        private set

    fun unlockDoor(key: Key): Boolean {
        // Return true if key is valid for door lock, false otherwise
    }
}

Mit einer Kombination aus Eigenschaften und Funktionen können Sie Klassen erstellen, alle Objekttypen modellieren.

Interoperabilität

Eine der wichtigsten Funktionen von Kotlin ist die fließende Interoperabilität mit Java. Da Kotlin-Code in JVM-Bytecode kompiliert wird, kann Ihr Kotlin-Code direkt in Java-Code ein und umgekehrt. Das bedeutet, dass Sie vorhandene Java-Bibliotheken direkt von Kotlin abrufen. Darüber hinaus hat die Mehrheit der Android APIs sind in Java geschrieben und können direkt aus Kotlin aufgerufen werden.

Nächste Schritte

Kotlin ist eine flexible, pragmatische Sprache mit zunehmender Unterstützung und Dynamik. Mi. Probieren Sie es einfach aus, falls Sie dies noch nicht getan haben. Die nächsten Schritte findest du in der offiziellen Kotlin-Dokumentation sowie den Leitfaden zur Häufige Kotlin-Muster in Ihren Android-Apps