Kotlin für große Teams anwenden

Der Wechsel zu einer neuen Sprache kann eine Herausforderung sein. Damit Ihr Team erfolgreich ist, sollten Sie langsam beginnen, in kleinen Schritten migrieren und häufig testen. Kotlin wird zu JVM-Bytecode kompiliert und ist vollständig mit Java kompatibel, was die Migration erleichtert.

Team aufbauen

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

Lerngruppen bilden

Lerngruppen sind eine effektive Möglichkeit, das Lernen und Behalten von Informationen zu erleichtern. Studien haben gezeigt dass das Wiederholen des Gelernten in einer Gruppe dazu beiträgt, das Material zu festigen. Besorgen Sie für jedes Mitglied der Gruppe ein Kotlin-Buch oder anderes Lernmaterial und bitten Sie die Gruppe, jede Woche ein paar Kapitel durchzugehen. Bei jedem Treffen sollte die Gruppe vergleichen, was sie gelernt hat, und alle Fragen oder Beobachtungen besprechen.

Eine Kultur des Lehrens schaffen

Nicht jeder sieht sich als Lehrer, aber jeder kann lehren. Von einem Technologie- oder Teamleiter bis hin zu einem einzelnen Mitarbeiter kann jeder ein Lernumfeld fördern, das zum Erfolg beitragen kann. Eine Möglichkeit, dies zu erleichtern, sind regelmäßige Präsentationen, bei denen eine Person im Team über etwas spricht, das sie gelernt hat oder teilen möchte. Sie können Ihre Lerngruppe nutzen, indem Sie Freiwillige bitten, jede Woche ein neues Kapitel zu präsentieren, bis sich Ihr Team mit der Sprache vertraut fühlt.

Champion benennen

Benennen Sie einen Champion, der die Lernbemühungen leitet. Diese Person kann als Fachexperte fungieren, wenn Sie mit der Einführung beginnen. Es ist wichtig, diese Person in alle Übungstreffen zu Kotlin einzubeziehen. Im Idealfall ist diese Person bereits von Kotlin begeistert und verfügt über einige praktische Kenntnisse.

Langsam integrieren

Es ist wichtig, langsam zu beginnen und strategisch zu überlegen, welche Teile Ihres Ökosystems zuerst migriert werden sollen. Oft ist es am besten, dies auf eine einzelne App in Ihrem Unternehmen zu beschränken, anstatt eine Flaggschiff-App zu verwenden. Was die Migration der ausgewählten App betrifft, ist jede Situation anders. Hier sind jedoch einige gängige Ausgangspunkte.

Datenmodell

Ihr Datenmodell besteht wahrscheinlich aus vielen Statusinformationen und einigen Methoden. Das Datenmodell kann auch allgemeine Methoden wie toString, equals und hashCode enthalten. Normalerweise können Sie diese Methoden migrieren und isoliert testen.

Nehmen wir zum Beispiel das folgende Java-Snippet an:

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 durch eine einzelne Zeile Kotlin ersetzen, wie im folgenden Beispiel gezeigt:

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

Anschließend können Sie diesen Code mit Ihrer aktuellen Testsuite testen. Beginnen Sie mit einem Modell nach dem anderen und migrieren Sie Klassen, die hauptsächlich Status und nicht Verhalten enthalten. Testen Sie häufig.

Tests migrieren

Ein weiterer Ausgangspunkt sind Ihre Tests: Konvertieren Sie vorhandene Tests und schreiben Sie neue Tests in Kotlin. So hat Ihr Team Zeit, sich mit der Sprache vertraut zu machen, bevor Sie Code schreiben, der mit Ihrer App ausgeliefert wird.

Hilfsmethoden in Erweiterungsfunktionen verschieben

Sie können statische Hilfsklassen wie StringUtils, IntegerUtils, DateUtils, oder YourCustomTypeUtils, als Kotlin-Erweiterungsfunktionen darstellen und sie in Ihrer vorhandenen Java-Codebasis verwenden.

Nehmen wir an, Sie haben eine StringUtils-Klasse mit einigen 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 App verwendet werden, wie im folgenden Beispiel gezeigt:

...

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

...

Mit Kotlin-Erweiterungsfunktionen können Sie Java-Aufrufern dieselbe Utils-Schnittstelle zur Verfügung stellen und gleichzeitig eine prägnantere API für Ihre wachsende Kotlin-Codebasis anbieten.

Dazu können Sie diese Utils-Klasse zuerst mit der automatischen Konvertierung der IDE in Kotlin konvertieren. Die Beispielausgabe könnte 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 verwenden Sie ihn, um auf den Typ innerhalb der Funktion zu verweisen, 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
}

Fügen Sie schließlich oben in der Quelldatei eine JvmName-Annotation hinzu, damit der kompilierte Name mit dem Rest Ihrer App kompatibel ist, wie im folgenden Beispiel gezeigt:

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

Die endgültige Version sollte 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
}

Beachten Sie, dass diese Funktionen jetzt mit Java oder Kotlin mit Konventionen aufgerufen werden können, die der jeweiligen Sprache entsprechen.

Kotlin

// ...

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

// ...

Java

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

Migration abschließen

Wenn sich Ihr Team mit Kotlin vertraut gemacht hat und Sie kleinere Bereiche migriert haben, können Sie zu größeren Komponenten wie Fragmenten, Aktivitäten, ViewModel-Objekten und anderen Klassen im Zusammenhang mit der Geschäftslogik übergehen.

Hinweise

So wie Java einen bestimmten Stil hat, hat Kotlin einen eigenen idiomatischen Stil, der zu seiner Prägnanz beiträgt. Anfangs sieht der Kotlin-Code, den Ihr Team schreibt, jedoch möglicherweise eher wie der Java-Code aus, den er ersetzt. Das ändert sich, wenn Ihr Team mehr Erfahrung mit Kotlin sammelt. Eine schrittweise Änderung ist der Schlüssel zum Erfolg.

Hier sind einige Dinge, die Sie tun können, um die Konsistenz Ihrer Kotlin-Codebasis zu gewährleisten, wenn sie wächst:

Allgemeine Codierungsstandards

Definieren Sie frühzeitig im Einführungsprozess einen Standardsatz von Codierungskonventionen. Sie können vom Android Kotlin-Stilhandbuch abweichen, wenn das sinnvoll ist.

Tools für die statische Analyse

Setzen Sie die für Ihr Team festgelegten Codierungsstandards mit Android Lint und anderen Tools für die statische Analyse durch. ktlint, ein Kotlin -Linter eines Drittanbieters, bietet auch zusätzliche Regeln für Kotlin.

Continuous Integration

Achten Sie darauf, dass Sie die allgemeinen Codierungsstandards einhalten und eine ausreichende Testabdeckung für Ihren Kotlin-Code bieten. Wenn Sie dies in einen automatisierten Build-Prozess einbeziehen, können Sie die Konsistenz und die Einhaltung dieser Standards gewährleisten.

Interoperabilität

Kotlin ist größtenteils nahtlos mit Java kompatibel. Beachten Sie jedoch die folgenden Punkte.

Null-Zulässigkeit

Kotlin verwendet Null-Zulässigkeits-Annotationen im kompilierten Code, um die Null-Zulässigkeit auf der Kotlin-Seite abzuleiten. Wenn keine Annotationen angegeben sind, verwendet Kotlin standardmäßig einen Plattformtyp, der entweder als null-zulässig oder nicht null-zulässig behandelt werden kann. Wenn Sie Plattformtypen nicht sorgfältig behandeln, kann dies zu NullPointerException-Problemen zur Laufzeit führen.

Neue Funktionen einführen

Kotlin bietet viele neue Bibliotheken und syntaktischen Zucker, um Boilerplate-Code zu reduzieren, was die Entwicklungs geschwindigkeit erhöht. Seien Sie jedoch vorsichtig und methodisch, wenn Sie die Standard bibliotheksfunktionen von Kotlin verwenden, z. B. Sammlungsfunktionen, Coroutinen, und Lambdas.

Hier ist eine häufige Falle, in die Entwickler geraten, die neu in Kotlin sind. Betrachten Sie den folgenden Kotlin-Code:

val nullableFoo: Foo? = 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 sollen foo.baz und foo.zap ausgeführt werden, wenn nullableFoo nicht null ist, um eine NullPointerException zu vermeiden. Dieser Code funktioniert zwar wie erwartet, ist aber weniger intuitiv zu lesen als eine Null-Prüfung und ein 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
}

Test

In Kotlin sind Klassen und ihre Funktionen standardmäßig für die Erweiterung geschlossen. Sie müssen die Klassen und Funktionen, die Sie unterklassen möchten, explizit öffnen. Kotlin trifft diese Entscheidung, um die Komposition gegenüber der Vererbung zu fördern. Um die Komposition zu vereinfachen, bietet Kotlin integrierte Unterstützung für die Implementierung von Verhalten durch Delegierung.

Dieses Verhalten stellt ein Problem für Mocking-Frameworks wie Mockito dar, die auf der Implementierung oder Vererbung von Schnittstellen basieren, um Verhaltensweisen während des Tests zu überschreiben. Für Unittests können Sie die Verwendung der Mock Maker Inline Funktion von Mockito aktivieren, mit der Sie finale Klassen und Methoden simulieren können. Alternativ können Sie das Compiler-Plug-in „All-Open“ verwenden, um jede Kotlin-Klasse und ihre Elemente zu öffnen, die Sie im Rahmen des Kompilierungsprozesses testen möchten. Der Hauptvorteil dieses Plug-ins besteht darin, dass es sowohl mit Unittests als auch mit Instrumentierungstests funktioniert.

Weitere Informationen

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