Zehn Kotlin-Funktionen zur Förderung der Android-Entwicklung

Veröffentlicht: 2022-03-11

Einführung

Vor einiger Zeit hat Tomasz die Kotlin-Entwicklung auf Android eingeführt. Zur Erinnerung: Kotlin ist eine neue Programmiersprache, die von Jetbrains entwickelt wurde, dem Unternehmen hinter einer der beliebtesten Java-IDEs, IntelliJ IDEA. Wie Java ist Kotlin eine Allzwecksprache. Da es dem Bytecode der Java Virtual Machine (JVM) entspricht, kann es Seite an Seite mit Java verwendet werden, ohne dass es zu Leistungseinbußen kommt.

In diesem Artikel werde ich die 10 nützlichsten Funktionen behandeln, um Ihre Android-Entwicklung anzukurbeln.

Hinweis : Zum Zeitpunkt der Erstellung dieses Artikels waren die aktuellen Versionen Android Studio 2.1.1. und Kotlin 1.0.2.

Kotlin

Müde von endlosem Java-Code? Probieren Sie Kotlin aus und sparen Sie Zeit und Verstand.
Twittern

Kotlin-Setup

Da Kotlin von JetBrains entwickelt wurde, wird es sowohl in Android Studio als auch in IntelliJ gut unterstützt.

Der erste Schritt ist die Installation des Kotlin-Plugins. Nachdem Sie dies erfolgreich getan haben, stehen neue Aktionen zum Konvertieren von Java in Kotlin zur Verfügung. Zwei neue Optionen sind:

  1. Erstellen Sie ein neues Android-Projekt und richten Sie Kotlin im Projekt ein.
  2. Kotlin-Unterstützung zu einem bestehenden Android-Projekt hinzufügen.

Um zu erfahren, wie Sie ein neues Android-Projekt erstellen, sehen Sie sich die offizielle Schritt-für-Schritt-Anleitung an. Um Kotlin-Unterstützung zu einem neu erstellten oder vorhandenen Projekt hinzuzufügen, öffnen Sie das Dialogfeld „Aktion suchen“ mit Command + Shift + A unter Mac oder Ctrl + Shift + A unter Windows/Linux und rufen Sie die Aktion Configure Kotlin in Project .

Um eine neue Kotlin-Klasse zu erstellen, wählen Sie:

  • File > New > Kotlin file/class oder
  • File > New > Kotlin activity

Alternativ können Sie eine Java-Klasse erstellen und diese mit der oben genannten Aktion in Kotlin konvertieren. Denken Sie daran, dass Sie damit jede Klasse, Schnittstelle, Aufzählung oder Anmerkung konvertieren können, und dies kann verwendet werden, um Java einfach mit Kotlin-Code zu vergleichen.

Ein weiteres nützliches Element, das viel Tipparbeit spart, sind Kotlin-Erweiterungen. Um sie zu verwenden, müssen Sie ein anderes Plugin in Ihrer Modul- build.gradle -Datei anwenden:

 apply plugin: 'kotlin-android-extensions'

Achtung : Wenn Sie die Kotlin-Plugin-Aktion zum Einrichten Ihres Projekts verwenden, wird der folgende Code in Ihre build.gradle -Datei der obersten Ebene eingefügt:

 buildscript { ext.kotlin_version = '1.0.2' repositories { jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }

Dies führt dazu, dass die Erweiterung nicht funktioniert. Um das zu beheben, kopieren Sie diesen Code einfach in jedes der Projektmodule, in denen Sie Kotlin verwenden möchten.

Wenn Sie alles richtig eingerichtet haben, sollten Sie Ihre Anwendung genauso ausführen und testen können wie in einem Standard-Android-Projekt, aber jetzt mit Kotlin.

Zeit sparen mit Kotlin

Beginnen wir also damit, einige Schlüsselaspekte der Kotlin-Sprache zu beschreiben und Tipps zu geben, wie Sie Zeit sparen können, indem Sie sie anstelle von Java verwenden.

Funktion Nr. 1: Statischer Layout-Import

Einer der häufigsten Boilerplate-Codes in Android ist die Verwendung der Funktion findViewById() , um Verweise auf Ihre Ansichten in Aktivitäten oder Fragmenten zu erhalten.

Es gibt Lösungen wie die Butterknife-Bibliothek, die etwas Tipparbeit sparen, aber Kotlin geht noch einen Schritt weiter, indem es Ihnen ermöglicht, alle Verweise auf Ansichten aus dem Layout mit einem Import zu importieren.

Betrachten Sie beispielsweise das folgende XML-Layout für Aktivitäten:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="co.ikust.kotlintest.MainActivity"> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>

Und der zugehörige Aktivitätscode:

 package co.ikust.kotlintest import android.support.v7.app.AppCompatActivity import android.os.Bundle import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) helloWorldTextView.text = "Hello World!" } }

Um die Referenzen für alle Ansichten im Layout mit einer definierten ID zu erhalten, verwenden Sie die Android Kotlin-Erweiterung Anko. Denken Sie daran, diese Importanweisung einzugeben:

 import kotlinx.android.synthetic.main.activity_main.*

Beachten Sie, dass Sie in Kotlin keine Semikolons am Ende der Zeilen schreiben müssen, da diese optional sind.

Die TextView aus dem Layout wird als TextView Instanz importiert, deren Name der ID der Ansicht entspricht. Lassen Sie sich nicht von der Syntax verwirren, die zum Setzen des Labels verwendet wird:

 helloWorldTextView.text = "Hello World!"

Wir werden das in Kürze behandeln.

Vorbehalte :

  • Stellen Sie sicher, dass Sie das richtige Layout importieren, andernfalls haben importierte Ansichtsreferenzen einen null .
  • Stellen Sie bei der Verwendung von Fragmenten sicher, dass importierte View-Referenzen nach dem Aufruf der Funktion onCreateView() verwendet werden. Importieren Sie das Layout in die Funktion onCreateView() und verwenden Sie die View-Referenzen, um die Benutzeroberfläche in onViewCreated() . Die Referenzen werden nicht zugewiesen, bevor die Methode onCreateView() beendet ist.

Feature Nr. 2: Schreiben von POJO-Klassen mit Kotlin

Etwas, das mit Kotlin am meisten Zeit spart, ist das Schreiben der POJO-Klassen (Plain Old Java Object), die zum Speichern von Daten verwendet werden. Beispielsweise im Anforderungs- und Antworttext einer RESTful-API. In Anwendungen, die auf die RESTful-API angewiesen sind, gibt es viele solcher Klassen.

In Kotlin wird viel für Sie erledigt, und die Syntax ist prägnant. Betrachten Sie beispielsweise die folgende Klasse in Java:

 public class User { private String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }

Wenn Sie mit Kotlin arbeiten, müssen Sie das öffentliche Schlüsselwort nicht erneut schreiben. Standardmäßig ist alles öffentlich. Wenn Sie beispielsweise eine Klasse deklarieren möchten, schreiben Sie einfach:

 class MyClass { }

Das Äquivalent des obigen Java-Codes in Kotlin:

 class User { var firstName: String? = null var lastName: String? = null }

Na, das spart viel Tipparbeit, oder? Lassen Sie uns den Kotlin-Code durchgehen.

Kotlin spart viel Tipparbeit

Beim Definieren von Variablen in Kotlin gibt es zwei Möglichkeiten:

  • Veränderliche Variablen, definiert durch das Schlüsselwort var .
  • Unveränderliche Variablen, definiert durch das Schlüsselwort val .

Als nächstes ist zu beachten, dass sich die Syntax ein wenig von Java unterscheidet; Zuerst deklarieren Sie den Variablennamen und folgen dann mit Typ. Außerdem sind Eigenschaften standardmäßig Nicht-Null-Typen, was bedeutet, dass sie keine null akzeptieren können. Um eine Variable so zu definieren, dass sie einen null akzeptiert, muss nach dem Typ ein Fragezeichen hinzugefügt werden. Wir werden später darüber und über Nullsicherheit in Kotlin sprechen.

Eine weitere wichtige Sache, die zu beachten ist, ist, dass Kotlin keine Felder für die Klasse deklarieren kann; Es können nur Eigenschaften definiert werden. In diesem Fall sind firstName und lastName also Eigenschaften, denen Standard-Getter/Setter-Methoden zugewiesen wurden. Wie bereits erwähnt, sind in Kotlin beide standardmäßig öffentlich.

Benutzerdefinierte Accessoren können geschrieben werden, zum Beispiel:

 class User { var firstName: String? = null var lastName: String? = null val fullName: String? get() firstName + " " + lastName }

Von außen verhalten sich Eigenschaften syntaktisch wie öffentliche Felder in Java:

 val userName = user.firstName user.firstName = "John"

Beachten Sie, dass die neue Eigenschaft fullName ist (definiert durch das Schlüsselwort val ) und einen benutzerdefinierten Getter hat; es hängt einfach den Vor- und Nachnamen an.

Alle Eigenschaften in Kotlin müssen zugewiesen werden, wenn sie deklariert werden oder sich in einem Konstruktor befinden. Es gibt Fälle, in denen das nicht bequem ist; beispielsweise für Eigenschaften, die über Abhängigkeitsinjektion initialisiert werden. In diesem Fall kann ein lateinit Modifikator verwendet werden. Hier ist ein Beispiel:

 class MyClass { lateinit var firstName : String; fun inject() { firstName = "John"; } }

Weitere Details zu Eigenschaften finden Sie in der offiziellen Dokumentation.

Feature Nr. 3: Klassenvererbung und Konstruktoren

Kotlin hat auch eine prägnantere Syntax, wenn es um Konstruktoren geht.

Konstrukteure

Kotlin-Klassen haben einen primären Konstruktor und einen oder mehrere sekundäre Konstruktoren. Ein Beispiel für die Definition eines primären Konstruktors:

 class User constructor(firstName: String, lastName: String) { }

Der primäre Konstruktor steht nach dem Klassennamen in der Klassendefinition. Wenn der primäre Konstruktor keine Anmerkungen oder Sichtbarkeitsmodifikatoren hat, kann das Konstruktorschlüsselwort weggelassen werden:

 class Person(firstName: String) { }

Beachten Sie, dass ein primärer Konstruktor keinen Code haben kann; Jede Initialisierung muss im init -Code-Block erfolgen:

 class Person(firstName: String) { init { //perform primary constructor initialization here } }

Darüber hinaus kann ein primärer Konstruktor verwendet werden, um Eigenschaften zu definieren und zu initialisieren:

 class User(var firstName: String, var lastName: String) { // ... }

Genau wie normale Eigenschaften können von einem primären Konstruktor definierte Eigenschaften unveränderlich ( val ) oder veränderlich ( var ) sein.

Klassen können auch sekundäre Konstruktoren haben; Die Syntax zum Definieren eines solchen lautet wie folgt:

 class User(var firstName: String, var lastName) { constructor(name: String, parent: Person) : this(name) { parent.children.add(this) } }

Beachten Sie, dass jeder sekundäre Konstruktor an einen primären Konstruktor delegieren muss. Dies ähnelt Java, das this Schlüsselwort verwendet:

 class User(val firstName: String, val lastName: String) { constructor(firstName: String) : this(firstName, "") { //... } }

Beachten Sie beim Instanziieren von Klassen, dass Kotlin im Gegensatz zu Java keine new Schlüsselwörter hat. Verwenden Sie zum Instanziieren der oben genannten User :

 val user = User("John", "Doe)

Einführung in die Vererbung

In Kotlin erweitern sich alle Klassen von Any , was Object in Java ähnelt. Standardmäßig sind Klassen geschlossen, wie finale Klassen in Java. Um eine Klasse zu erweitern, muss sie also als open oder abstract deklariert werden:

 open class User(val firstName, val lastName) class Administrator(val firstName, val lastName) : User(firstName, lastName)

Beachten Sie, dass Sie an den Standardkonstruktor der erweiterten Klasse delegieren müssen, was dem Aufrufen der Methode super() im Konstruktor einer neuen Klasse in Java ähnelt.

Weitere Einzelheiten zu den Klassen finden Sie in der offiziellen Dokumentation.

Funktion Nr. 4: Lambda-Ausdrücke

Lambda-Ausdrücke, die mit Java 8 eingeführt wurden, sind eine seiner Lieblingsfunktionen. Auf Android sieht es jedoch nicht so gut aus, da es immer noch nur Java 7 unterstützt und es so aussieht, als ob Java 8 in absehbarer Zeit nicht unterstützt wird. Problemumgehungen wie Retrolambda bringen also Lambda-Ausdrücke auf Android.

Mit Kotlin sind keine zusätzlichen Bibliotheken oder Problemumgehungen erforderlich.

Funktionen in Kotlin

Beginnen wir damit, die Funktionssyntax in Kotlin schnell durchzugehen:

 fun add(x: Int, y: Int) : Int { return x + y }

Der Rückgabewert der Funktion kann weggelassen werden, und in diesem Fall gibt die Funktion Int zurück. Es lohnt sich zu wiederholen, dass alles in Kotlin ein Objekt ist, das von Any erweitert wurde, und dass es keine primitiven Typen gibt.

Ein Argument der Funktion kann einen Standardwert haben, zum Beispiel:

 fun add(x: Int, y: Int = 1) : Int { return x + y; }

In diesem Fall kann die Funktion add() aufgerufen werden, indem nur das x -Argument übergeben wird. Der äquivalente Java-Code wäre:

 int add(int x) { Return add(x, 1); } int add(int x, int y) { return x + y; }

Eine weitere nette Sache beim Aufruf einer Funktion ist, dass benannte Argumente verwendet werden können. Zum Beispiel:

 add(y = 12, x = 5)

Weitere Einzelheiten zu den Funktionen finden Sie in der offiziellen Dokumentation.

Verwenden von Lambda-Ausdrücken in Kotlin

Lambda-Ausdrücke in Kotlin können als anonyme Funktionen in Java angesehen werden, jedoch mit einer prägnanteren Syntax. Lassen Sie uns als Beispiel zeigen, wie Sie einen Klick-Listener in Java und Kotlin implementieren.

In Java:

 view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(v.getContext(), "Clicked on view", Toast.LENGTH_SHORT).show(); } };

In Kotlin:

 view.setOnClickListener({ view -> toast("Click") })

Beeindruckend! Nur eine Codezeile! Wir können sehen, dass der Lambda-Ausdruck von geschweiften Klammern umgeben ist. Parameter werden zuerst deklariert, und der Körper kommt nach dem -> Zeichen. Beim Klick-Listener wird der Typ für den Ansichtsparameter nicht angegeben, da er abgeleitet werden kann. Der Körper ist einfach ein Aufruf der Funktion toast() zum Anzeigen von Toast, den Kotlin bereitstellt.

Wenn Parameter nicht verwendet werden, können wir sie auch weglassen:

 view.setOnClickListener({ toast("Click") })

Kotlin hat Java-Bibliotheken optimiert, und jede Funktion, die eine Schnittstelle mit einer Methode für ein Argument empfängt, kann mit einem Funktionsargument (anstelle von Interface) aufgerufen werden.

Wenn die Funktion der letzte Parameter ist, kann sie außerdem aus den Klammern verschoben werden:

 view.setOnClickListener() { toast("Click") }

Wenn die Funktion schließlich nur einen Parameter hat, der eine Funktion ist, können Klammern weggelassen werden:

 view.setOnClickListener { toast("Click") }

Weitere Informationen finden Sie im Buch Kotlin für Android-Entwickler von Antonio Leiva und in der offiziellen Dokumentation.

Erweiterungsfunktionen

Kotlin bietet ähnlich wie C# die Möglichkeit, vorhandene Klassen mithilfe von Erweiterungsfunktionen um neue Funktionen zu erweitern. Zum Beispiel eine Erweiterungsmethode, die den MD5-Hash eines String berechnen würde:

 fun String.md5(): ByteArray { val digester = MessageDigest.getInstance("MD5") digester.update(this.toByteArray(Charset.defaultCharset())) return digester.digest() }

Beachten Sie, dass dem Funktionsnamen der Name der erweiterten Klasse (in diesem Fall String ) vorangestellt ist und dass die Instanz der erweiterten Klasse über this Schlüsselwort verfügbar ist.

Erweiterungsfunktionen sind das Äquivalent zu Java-Dienstprogrammfunktionen. Die Beispielfunktion in Java würde wie folgt aussehen:

 public static int toNumber(String instance) { return Integer.valueOf(instance); }

Die Beispielfunktion muss in einer Utility-Klasse platziert werden. Das bedeutet, dass Erweiterungsfunktionen die ursprüngliche erweiterte Klasse nicht modifizieren, aber eine bequeme Art sind, Utility-Methoden zu schreiben.

Funktion Nr. 5: Null-Sicherheit

Eines der Dinge, mit denen Sie in Java am meisten zu tun haben, ist wahrscheinlich NullPointerException . Nullsicherheit ist eine Funktion, die in die Kotlin-Sprache integriert wurde und so implizit ist, dass Sie sich normalerweise keine Sorgen machen müssen. Die offizielle Dokumentation besagt, dass die einzigen möglichen Ursachen für NullPointerExceptions sind:

  • Ein expliziter Aufruf zum Auslösen von NullPointerException .
  • Mit dem !! Operator (den ich später erklären werde).
  • Externer Java-Code.
  • Wenn auf die lateinit Eigenschaft im Konstruktor zugegriffen wird, bevor sie initialisiert wurde, wird eine UninitializedPropertyAccessException ausgelöst.

Standardmäßig gelten alle Variablen und Eigenschaften in Kotlin als non-null (können keinen null enthalten), wenn sie nicht explizit als nullable deklariert sind. Wie bereits erwähnt, muss ein Fragezeichen nach dem Typ hinzugefügt werden, um eine Variable so zu definieren, dass sie einen null akzeptiert. Zum Beispiel:

 val number: Int? = null

Beachten Sie jedoch, dass der folgende Code nicht kompiliert wird:

 val number: Int? = null number.toString()

Dies liegt daran, dass der Compiler null durchführt. Zum Kompilieren muss eine null hinzugefügt werden:

 val number: Int? = null if(number != null) { number.toString(); }

Dieser Code wird erfolgreich kompiliert. Was Kotlin in diesem Fall im Hintergrund macht, ist, dass diese number innerhalb des if-Blocks nun-null ( Int statt Int? ) wird.

Die null kann mit dem Safe-Call-Operator ( ?. ) vereinfacht werden:

 val number: Int? = null number?.toString()

Die zweite Zeile wird nur ausgeführt, wenn die Zahl nicht null ist. Sie können sogar den berühmten Elvis-Operator ( ?: ) verwenden:

 val number Int? = null val stringNumber = number?.toString() ?: "Number is null"

Wenn der Ausdruck links von ?: nicht null ist, wird er ausgewertet und zurückgegeben. Andernfalls wird das Ergebnis des Ausdrucks rechts zurückgegeben. Eine weitere nette Sache ist, dass Sie throw oder return auf der rechten Seite des Elvis-Operators verwenden können, da es sich um Ausdrücke in Kotlin handelt. Zum Beispiel:

 fun sendMailToUser(user: User) { val email = user?.email ?: throw new IllegalArgumentException("User email is null") //... }

Die !! Operator

Wenn Sie möchten, dass eine NullPointerException auf die gleiche Weise wie in Java geworfen wird, können Sie dies mit dem !! Operator. Der folgende Code löst eine NullPointerException :

 val number: Int? = null number!!.toString()

Gießen

Das Casting erfolgt mit einem as -Schlüsselwort:

 val x: String = y as String

Dies wird als „unsicheres“ Casting betrachtet, da es ClassCastException , wenn das Casting nicht möglich ist, wie dies bei Java der Fall ist. Es gibt einen „Safe“-Cast-Operator, der den null zurückgibt, anstatt eine Ausnahme auszulösen:

 val x: String = y as? String

Weitere Details zum Casting finden Sie im Abschnitt Type Casts und Casts der offiziellen Dokumentation, und für weitere Details zur null -Sicherheit lesen Sie den Abschnitt Null-Safety.

lateinit Eigenschaften

Es gibt einen Fall, in dem die Verwendung von lateinit -Eigenschaften eine Ausnahme ähnlich wie NullPointerException verursachen kann. Betrachten Sie die folgende Klasse:

 class InitTest { lateinit var s: String; init { val len = this.s.length } }

Dieser Code wird ohne Warnung kompiliert. Sobald jedoch eine Instanz von TestClass erstellt wird, wird eine UninitializedPropertyAccessException ausgelöst, da auf die Eigenschaft s zugegriffen wird, bevor sie initialisiert wird.

Funktion Nr. 6: Funktion with()

Die Funktion with() ist nützlich und wird mit der Kotlin-Standardbibliothek geliefert. Es kann verwendet werden, um Tipparbeit zu sparen, wenn Sie auf viele Eigenschaften eines Objekts zugreifen müssen. Zum Beispiel:

 with(helloWorldTextView) { text = "Hello World!" visibility = View.VISIBLE }

Als Parameter erhält sie ein Objekt und eine Erweiterungsfunktion. Der Codeblock (in den geschweiften Klammern) ist ein Lambda-Ausdruck für die Erweiterungsfunktion des als erster Parameter angegebenen Objekts.

Funktion Nr. 7: Bedienerüberladung

Mit Kotlin können benutzerdefinierte Implementierungen für einen vordefinierten Satz von Operatoren bereitgestellt werden. Um einen Operator zu implementieren, muss eine Elementfunktion oder eine Erweiterungsfunktion mit dem angegebenen Namen bereitgestellt werden.

Um beispielsweise den Multiplikationsoperator zu implementieren, muss eine Elementfunktion oder Erweiterungsfunktion mit dem Namen times(argument) bereitgestellt werden:

 operator fun String.times(b: Int): String { val buffer = StringBuffer() for (i in 1..b) { buffer.append(this) } return buffer.toString() }

Das obige Beispiel zeigt eine Implementierung des binären Operators * für den String . Der folgende Ausdruck weist beispielsweise einer newString Variablen den Wert „TestTestTestTest“ zu:

 val newString = "Test" * 4

Da Erweiterungsfunktionen verwendet werden können, bedeutet dies, dass das Standardverhalten der Operatoren für alle Objekte geändert werden kann. Dies ist ein zweischneidiges Schwert und sollte mit Vorsicht verwendet werden. Eine Liste der Funktionsnamen für alle Operatoren, die überladen werden können, finden Sie in der offiziellen Dokumentation.

Ein weiterer großer Unterschied zu Java sind die Operatoren == und != . Operator == bedeutet übersetzt:

 a?.equals(b) ?: b === null

Operator != bedeutet:

 !(a?.equals(b) ?:

Das bedeutet, dass die Verwendung von == keine Identitätsprüfung wie in Java durchführt (vergleichen Sie, ob Instanzen eines Objekts gleich sind), sondern sich genauso verhält wie die Methode equals() zusammen mit null .

Um eine Identitätsprüfung durchzuführen, müssen die Operatoren === und !== in Kotlin verwendet werden.

Funktion Nr. 8: Delegierte Eigenschaften

Bestimmte Eigenschaften weisen einige allgemeine Verhaltensweisen auf. Zum Beispiel:

  • Lazy initialisierte Eigenschaften, die beim ersten Zugriff initialisiert werden.
  • Eigenschaften, die Observable im Observer-Muster implementieren.
  • Eigenschaften, die stattdessen als separate Felder in einer Karte gespeichert werden.

Um solche Fälle einfacher implementieren zu können, unterstützt Kotlin Delegated Properties :

 class SomeClass { var p: String by Delegate() }

Das bedeutet, dass Getter- und Setter-Funktionen für die Eigenschaft p von einer Instanz einer anderen Klasse, Delegate , verarbeitet werden.

Ein Beispiel für einen Delegaten für die String Eigenschaft:

 class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, thank you for delegating '${property.name}' to me!" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$value has been assigned to '${property.name} in $thisRef.'") } }

Das obige Beispiel gibt eine Nachricht aus, wenn eine Eigenschaft zugewiesen oder gelesen wird.

Delegaten können sowohl für änderbare ( var ) als auch für schreibgeschützte ( val ) Eigenschaften erstellt werden.

Für eine schreibgeschützte Eigenschaft muss die getValue -Methode implementiert werden. Es benötigt zwei Parameter (aus der offiziellen Dokumentation entnommen):

  • Empfänger - muss derselbe oder ein Supertyp des Eigentümers der Eigenschaft sein (bei Erweiterungseigenschaften ist dies der Typ, der erweitert wird).
  • Metadaten - müssen vom Typ KProperty<*> oder dessen Supertyp sein.

Diese Funktion muss denselben Typ wie Eigenschaft oder ihren Untertyp zurückgeben.

Für eine veränderliche Eigenschaft muss ein Delegat zusätzlich eine Funktion namens setValue , die die folgenden Parameter akzeptiert:

  • Empfänger - dasselbe wie für getValue() .
  • Metadaten - wie bei getValue() .
  • neuer Wert - muss vom gleichen Typ wie eine Eigenschaft oder ihr Supertyp sein.

Es gibt einige Standard-Delegates, die mit Kotlin geliefert werden und die häufigsten Situationen abdecken:

  • Faul
  • Beobachtbar
  • Vetofähig

Faul

Lazy ist ein Standarddelegat, der einen Lambda-Ausdruck als Parameter akzeptiert. Der übergebene Lambda-Ausdruck wird beim ersten Aufruf der Methode getValue() ausgeführt.

Standardmäßig wird die Auswertung von faulen Eigenschaften synchronisiert. Wenn Sie sich nicht mit Multithreading befassen, können Sie lazy(LazyThreadSafetyMode.NONE) { … } verwenden, um zusätzliche Leistung zu erzielen.

Beobachtbar

Die Delegates.observable() ist für Eigenschaften, die sich als Observables im Observer-Muster verhalten sollen. Es akzeptiert zwei Parameter, den Anfangswert und eine Funktion mit drei Argumenten (Eigenschaft, alter Wert und neuer Wert).

Der angegebene Lambda-Ausdruck wird jedes Mal ausgeführt, wenn die Methode setValue() aufgerufen wird:

 class User { var email: String by Delegates.observable("") { prop, old, new -> //handle the change from old to new value } }

Vetofähig

Dieser Standard-Delegat ist eine besondere Art von Observable, mit dem Sie entscheiden können, ob ein neuer Wert, der einer Eigenschaft zugewiesen wird, gespeichert wird oder nicht. Es kann verwendet werden, um einige Bedingungen zu überprüfen, bevor ein Wert zugewiesen wird. Wie bei Delegates.observable() akzeptiert es zwei Parameter: den Anfangswert und eine Funktion.

Der Unterschied besteht darin, dass die Funktion einen booleschen Wert zurückgibt. Wenn sie true zurückgibt, wird der der Eigenschaft zugewiesene neue Wert gespeichert oder anderweitig verworfen.

 var positiveNumber = Delegates.vetoable(0) { d, old, new -> new >= 0 }

Das angegebene Beispiel speichert nur positive Zahlen, die der Eigenschaft zugewiesen sind.

Weitere Einzelheiten finden Sie in der offiziellen Dokumentation.

Feature Nr. 9: Zuordnen eines Objekts zu einer Karte

Ein häufiger Anwendungsfall besteht darin, Werte der Eigenschaften in einer Karte zu speichern. Dies geschieht häufig in Anwendungen, die mit RESTful-APIs arbeiten und JSON-Objekte parsen. In diesem Fall kann eine Karteninstanz als Stellvertreter für eine delegierte Eigenschaft verwendet werden. Ein Beispiel aus der offiziellen Dokumentation:

 class User(val map: Map<String, Any?>) { val name: String by map val age: Int by map }

In diesem Beispiel hat der User einen primären Konstruktor, der eine Zuordnung übernimmt. Die beiden Eigenschaften übernehmen die Werte aus der Zuordnung, die Schlüsseln zugeordnet sind, die den Eigenschaftsnamen entsprechen:

 val user = User(mapOf( "name" to "John Doe", "age" to 25 ))

Der Namenseigenschaft der neuen Benutzerinstanz wird der Wert „John Doe“ und der Alterseigenschaft der Wert 25 zugewiesen.

Dies funktioniert auch für var-Eigenschaften in Kombination mit MutableMap :

 class MutableUser(val map: MutableMap<String, Any?>) { var name: String by map var age: Int by map }

Feature Nr. 10: Sammlungen und funktionale Operationen

Mit der Unterstützung für Lambdas in Kotlin können Sammlungen auf eine neue Ebene gebracht werden.

Zunächst einmal unterscheidet Kotlin zwischen veränderlichen und unveränderlichen Sammlungen. Beispielsweise gibt es zwei Versionen der Iterable- Schnittstelle:

  • Wiederholbar
  • MutableIterable

Dasselbe gilt für die Interfaces Collection , List , Set und Map .

Beispielsweise gibt diese any Operation true zurück, wenn mindestens ein Element mit dem angegebenen Prädikat übereinstimmt:

 val list = listOf(1, 2, 3, 4, 5, 6) assertTrue(list.any { it % 2 == 0 })

Eine ausführliche Liste der funktionalen Vorgänge, die für Sammlungen ausgeführt werden können, finden Sie in diesem Blogbeitrag.

Fazit

Wir haben gerade erst an der Oberfläche dessen gekratzt, was Kotlin bietet. Für diejenigen, die an weiterer Lektüre interessiert sind und mehr erfahren möchten, überprüfen Sie:

  • Kotlin-Blogbeiträge und -Buch von Antonio Leiva.
  • Offizielle Dokumentation und Tutorials von JetBrains.

Zusammenfassend bietet Ihnen Kotlin die Möglichkeit, beim Schreiben nativer Android-Anwendungen Zeit zu sparen, indem Sie eine intuitive und prägnante Syntax verwenden. Es ist noch eine junge Programmiersprache, aber meiner Meinung nach ist sie jetzt stabil genug, um für die Erstellung von Produktions-Apps verwendet zu werden.

Die Vorteile der Verwendung von Kotlin:

  • Die Unterstützung durch Android Studio ist nahtlos und ausgezeichnet.
  • Es ist einfach, ein vorhandenes Java-Projekt in Kotlin zu konvertieren.
  • Java- und Kotlin-Code können im selben Projekt koexistieren.
  • Es gibt keinen Geschwindigkeits-Overhead in der Anwendung.

Die Nachteile:

  • Kotlin fügt seine Bibliotheken der generierten .apk , sodass die endgültige .apk Größe etwa 300 KB größer sein wird.
  • Bei Missbrauch kann das Überladen von Operatoren zu unlesbarem Code führen.
  • IDE und Autocomplete verhalten sich bei der Arbeit mit Kotlin etwas langsamer als bei reinen Java-Android-Projekten.
  • Die Kompilierungszeiten können etwas länger sein.