Kotlin für große Teams anwenden

Der Wechsel zu einer neuen Sprache kann eine gewaltige Aufgabe sein. Das Erfolgsrezept besteht darin, langsam anzufangen, in Blöcken aufzuteilen und häufig zu testen, um Ihr Team auf Erfolgskurs zu bringen. Kotlin erleichtert die Migration, da es zu JVM-Bytecode kompiliert wird und vollständig interoperabel mit Java ist.

Aufbau des Teams

Der erste Schritt vor der Migration besteht darin, ein gemeinsames Grundverständnis für Ihr Team aufzubauen. Hier sind einige Tipps, die Ihnen helfen können, das Lernen Ihres Teams zu beschleunigen.

Lerngruppen

Lerngruppen sind ein effektives Mittel, um das Lernen und die Bindung zu fördern. Studien zufolge wird das Gelernte in einer Gruppenumgebung festgehalten, um das Gelernte zu untermauern. Laden Sie für jedes Gruppenmitglied ein Kotlin-Buch oder anderes Lernmaterial herunter und bitten Sie die Gruppe, jede Woche ein paar Kapitel durchzugehen. Bei jedem Meeting sollte die Gruppe das Gelernte vergleichen und eventuelle Fragen oder Beobachtungen besprechen.

Eine Lehrkultur schaffen

Nicht jeder sieht sich selbst als Lehrer an, doch jeder kann unterrichten. Von einer Technologie oder einem Team, das zu einem einzelnen Mitwirkenden führt, kann jeder eine Lernumgebung schaffen, die zum Erfolg beitragen kann. Eine Möglichkeit dafür sind regelmäßige Präsentationen, bei denen eine Person im Team über das Gelernte sprechen oder darüber sprechen möchte. Sie können Ihre Studiengruppe nutzen, indem Sie Freiwillige bitten, jede Woche ein neues Kapitel vorzutragen, bis Sie an einem Punkt sind, an dem Ihr Team mit der Sprache vertraut ist.

Einen Champion ernennen

Legen Sie schließlich einen Champion fest, der die Lernleistung leitet. Diese Person kann als Fachleute fungieren, die Ihnen bei der Einführung helfen. Es ist wichtig, diese Person in alle Ihre Übungsmeetings zu Kotlin einzubeziehen. Im Idealfall begeistert sich diese Person bereits für Kotlin und verfügt über einige Arbeitskenntnisse.

Langsam integrieren

Es ist wichtig, langsam anzufangen und strategisch darüber nachzudenken, welche Teile Ihres Ökosystems zuerst zu tun sein sollten. Es ist oft am besten, dies auf eine einzelne Anwendung innerhalb Ihrer Organisation und nicht auf eine Flagship-Anwendung zu beschränken. In Bezug auf die Migration der ausgewählten Anwendung ist jede Situation anders, aber hier sind einige gängige Methoden, mit denen Sie beginnen können.

Datenmodell

Ihr Datenmodell besteht wahrscheinlich aus vielen Statusinformationen und einigen Methoden. Das Datenmodell kann auch gängige Methoden wie toString(), equals() und hashcode() enthalten. Diese Methoden können in der Regel ganz einfach umgestellt und Einheiten getestet werden.

Nehmen wir zum Beispiel das folgende Java-Snippet:

public class Person {

   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;
   }

   @Override
   public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;
       Person person = (Person) o;
       return Objects.equals(firstName, person.firstName) &&
               Objects.equals(lastName, person.lastName);
   }

   @Override
   public int hashCode() {
       return Objects.hash(firstName, lastName);
   }

   @Override
   public String toString() {
       return "Person{" +
               "firstName='" + firstName + '\'' +
               ", lastName='" + lastName + '\'' +
               '}';
   }
}

Sie können die Java-Klasse wie hier gezeigt durch eine einzelne Zeile von Kotlin ersetzen:

data class Person(var firstName: String?, var lastName : String?)

Dieser Code kann dann mit deiner aktuellen Testsuite getestet werden. Die Idee hierbei ist, klein mit einem Modell nach dem anderen zu beginnen und Klassen zu wechseln, die größtenteils Status und kein Verhalten sind. Führen Sie während des Tests regelmäßig Tests durch.

Tests migrieren

Eine weitere mögliche Option ist die Konvertierung vorhandener Tests und das Schreiben neuer Tests in Kotlin. So hat Ihr Team Zeit, sich mit der Sprache vertraut zu machen, bevor es Code schreibt, den Sie mit Ihrer App ausliefern möchten.

Dienstprogrammmethoden in Erweiterungsfunktionen verschieben

Alle statischen Dienstprogrammklassen (StringUtils, IntegerUtils, DateUtils, YourCustomTypeUtils usw.) können als Kotlin-Erweiterungsfunktionen dargestellt und von Ihrer vorhandenen Java-Codebasis verwendet werden.

Angenommen, Sie haben eine StringUtils-Klasse mit mehreren Methoden:

package com.java.project;

public class StringUtils {

   public static String foo(String receiver) {
       return receiver...;  // Transform the receiver in some way
   }

   public static String bar(String receiver) {
       return receiver...;  // Transform the receiver in some way
   }

}

Diese Methoden können dann an anderer Stelle in Ihrer Anwendung verwendet werden, wie im folgenden Beispiel gezeigt:

...

String myString = ...
String fooString = StringUtils.foo(myString);

...

Mit Kotlin-Erweiterungsfunktionen können Sie Java-Aufrufern die gleiche Utils-Schnittstelle bieten und gleichzeitig eine kürzere API für Ihre wachsende Kotlin-Codebasis bereitstellen.

Dazu könnten Sie diese Utils-Klasse mithilfe der automatischen Konvertierung der IDE in Kotlin umwandeln. Die Beispielausgabe könnte etwa so aussehen:

package com.java.project

object StringUtils {

   fun foo(receiver: String): String {
       return receiver...;  // Transform the receiver in some way
   }

   fun bar(receiver: String): String {
       return receiver...;  // Transform the receiver in some way
   }

}

Entfernen Sie als Nächstes die Klassen- oder Objektdefinition, stellen Sie jedem Funktionsnamen den Typ voran, auf den diese Funktion angewendet werden soll, und verweisen Sie damit auf den Typ innerhalb der Funktion, wie im folgenden Beispiel gezeigt:

package com.java.project

fun String.foo(): String {
    return this...;  // Transform the receiver in some way
}

fun String.bar(): String {
    return this...;  // Transform the receiver in some way
}

Zum Schluss fügen Sie am Anfang der Quelldatei eine JvmName-Annotation hinzu, damit der kompilierte Name mit dem Rest Ihrer Anwendung kompatibel ist, wie im folgenden Beispiel gezeigt:

@file:JvmName("StringUtils")
package com.java.project
...

Die endgültige Version sollte in etwa so aussehen:

@file:JvmName("StringUtils")
package com.java.project

fun String.foo(): String {
    return this...;  // Transform `this` string in some way
}

fun String.bar(): String {
    return this...;  // Transform `this` string in some way
}

Diese Funktionen können jetzt mit Java oder Kotlin mit Konventionen aufgerufen werden, die jeder Sprache entsprechen.

Kotlin

...
val myString: String = ...
val fooString = myString.foo()
...

Java

...
String myString = ...
String fooString = StringUtils.foo(myString);
...

Migration abschließen

Sobald Ihr Team mit Kotlin vertraut ist und Sie kleinere Bereiche migriert haben, können Sie mit größeren Komponenten wie Fragmenten, Aktivitäten, ViewModel-Objekten und anderen Klassen im Zusammenhang mit der Geschäftslogik fortfahren.

Wissenswertes

Ähnlich wie Java über einen speziellen Stil verfügt Kotlin über einen eigenen idiomatischen Stil, der zu seiner Präzision beiträgt. Vielleicht stellen Sie jedoch anfangs fest, dass der Kotlin-Code, den Ihr Team erstellt, eher dem Java-Code ähnelt, den es ersetzt. Das ändert sich mit der Zeit, wenn Ihr Team immer mehr Erfahrung mit Kotlin hat. Denken Sie daran, dass schrittweise Änderungen der Schlüssel zum Erfolg sind.

Hier sind einige Dinge, die Sie tun können, um Konsistenz zu erreichen, wenn Ihre Kotlin-Codebasis größer wird:

Gängige Programmierstandards

Definieren Sie frühzeitig im Einführungsprozess einen Standardsatz von Codierungskonventionen. Wo es sinnvoll ist, können Sie vom Kotlin-Styleguide für Android abweichen.

Statische Analysetools

Mit Android Lint und anderen statischen Analysetools können Sie die Coding-Standards für Ihr Team erzwingen. klint, ein Kotlin-Linter eines Drittanbieters, bietet zusätzliche Regeln für Kotlin.

Kontinuierliche Integration

Achten Sie darauf, die gängigen Codierungsstandards zu erfüllen, und bieten Sie genügend Testabdeckung für Ihren Kotlin-Code. Wenn Sie dies in einen automatisierten Build-Prozess einbinden, können Sie Konsistenz und Einhaltung dieser Standards gewährleisten.

Interoperabilität

Kotlin funktioniert größtenteils nahtlos mit Java. Beachten Sie jedoch Folgendes:

Null-Zulässigkeit

Kotlin stützt sich auf Annotationen zur Null-Zulässigkeit in kompiliertem Code, um die Null-Zulässigkeit auf der Kotlin-Seite abzuleiten. Wenn keine Annotationen vorhanden sind, verwendet Kotlin standardmäßig einen Plattformtyp, der als Typ mit Null- oder Null-Zulässigkeit behandelt werden kann. Dies kann jedoch zu NullPointerException-Laufzeitproblemen führen, wenn dies nicht sorgfältig behandelt wird.

Neue Funktionen einführen

Kotlin bietet viele neue Bibliotheken und syntaktischen Zucker, um den Boilerplate-Standard zu reduzieren und so die Entwicklungsgeschwindigkeit zu erhöhen. Seien Sie jedoch vorsichtig und methodisch, wenn Sie die Standardbibliotheksfunktionen von Kotlin wie Erfassungsfunktionen, Koroutinen und Lambdas verwenden.

Hier ist ein häufiger Fehler, auf den neuere Kotlin-Entwickler stoßen. Angenommen, der folgende Kotlin-Code lautet:

val nullableFoo: Foo? = ...

// This lambda executes only if nullableFoo is not null
// and `foo` is of the non-nullable Foo type
nullableFoo?.let { foo ->
   foo.baz()
   foo.zap()
}

In diesem Beispiel werden foo.baz() und foo.zap() ausgeführt, wenn nullableFoo nicht null ist. So wird ein NullPointerException vermieden. Dieser Code funktioniert zwar wie erwartet, ist aber weniger intuitiv zu lesen als eine einfache Nullprüfung und Smart Cast, wie im folgenden Beispiel gezeigt:

val nullableFoo: Foo? = null
if (nullableFoo != null) {
    nullableFoo.baz() // Using !! or ?. isn't required; the Kotlin compiler infers non-nullability
    nullableFoo.zap() // from guard condition; smart casts nullableFoo to Foo inside this block
}

Testen

Klassen und ihre Funktionen sind in Kotlin standardmäßig für Erweiterungen geschlossen. Sie müssen die Klassen und Funktionen, für die Sie abgeleitete Klassen erstellen möchten, explizit öffnen. Dieses Verhalten ist eine Entscheidung bezüglich des Sprachdesigns, mit der die Komposition gegenüber Vererbung gefördert werden soll. Kotlin unterstützt die Delegierung, um die Zusammensetzung zu vereinfachen.

Dies stellt ein Problem bei Mocking-Frameworks wie Mockito dar, bei denen eine Schnittstellenimplementierung oder eine Übernahme erforderlich ist, um das Verhalten während des Tests zu überschreiben. Für Unittests können Sie die Mock Maker Inline-Funktion von Mockito aktivieren, um endgültige Klassen und Methoden zu simulieren. Alternativ können Sie das All-Open-Compiler-Plug-in verwenden, um eine Kotlin-Klasse und ihre Mitglieder zu öffnen, die Sie im Rahmen der Kompilierung testen möchten. Der Hauptvorteil dieses Plug-ins besteht darin, dass es sowohl mit Einheitentests als auch mit instrumentierten Tests funktioniert.

Weitere Informationen

Weitere Informationen zur Verwendung von Kotlin finden Sie unter den folgenden Links: