Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

大規模なチームに Kotlin を導入する

Kotlin は、ラムダのサポート、型システムの中核的要素としての null 値許容、最大級のプロパティ サポート、コルーチンなど、簡潔な構文と複数の最新の機能を提供するプログラミング言語です。Kotlin は、シングルトン パターンやデリゲート パターンなど、多くの一般的デザイン パターンをネイティブにサポートしています。

言語機能に加え、Android Studio のバックボーンである IntelliJ のメーカー、JetBrains によって作成されサポートされています。この IDE 統合により、Kotlin 開発に多くのサポートが提供され、チームの生産性向上が可能になっています。

Android 上の Kotlin

Kotlin の簡潔な構文と、既存の API を簡単に拡張できる能力は、新規および既存のどちらの Android API でも有用です。Kotlin は、Android KTX 拡張プロジェクトを通じて、多くの中核的な Android API にすでに取り入れられています。さらに、Java と Kotlin の両方をサポートする Jetpack コンポーネントの多くには、Kotlin デベロッパー向けの慣用的 API を提供するライブラリの KTX バージョンが含まれています。また、Jetpack Benchmark ライブラリなどの多くのライブラリは Kotlin で記述されています。

同時実行

Kotlin は、コルーチンを使用した非同期プログラミングをサポートしています。Android は、LiveDataViewModelLifecycleWorkManager など、数多くのアーキテクチャ コンポーネントで、Kotlin コルーチン向けのファースト クラスのサポートも提供しています。詳しくは、アーキテクチャ コンポーネントで Kotlin コルーチンを使用するをご覧ください。

プログラミング言語を組織レベルで検討する際には、長期的な視点で検討してください。SlashData によると、Kotlin は現在、世界で最も急成長している言語です。

業界の潮流が Kotlin に向かうのを待っていたのなら、今こそこの流れに飛び込む絶好のタイミングです。

移行の道筋

新しい言語への移行には大きな困難が伴います。成功のこつは、最初から多くを求めず、段階的に進み、頻繁なテストによってチームを成功に向けて動かすことにあります。 Kotlin は JVM バイトコードにコンパイルされ、Java との完全な相互運用が可能であるため、これは通常簡単に実行できます。

チームの構築

移行前の最初のステップは、チーム内で共通の基本的理解を構築することです。始めるためのリソースは多数あります。ここでは、チームの学習促進に役立つヒントをいくつかご紹介します。

学習グループの形成

学習グループは、学習と定着を円滑に進めるための効果的な方法です。研究によると、集団で学んだことを復唱すると内容の補強に役立つとされています。グループの各メンバーに Kotlin の書籍などの学習資料を用意し、毎週数章を読むようグループに指示してください。それぞれの会合において、グループ内で、学んだ内容を比較し、質問や意見を話し合うとよいでしょう。

教える文化を築く

人に教えるのがうまいと自覚している人は少ないとしても、教えることは誰にでもできます。テクノロジー リーダーやチームリーダーから個々のコントリビューターに至るまで、誰もが成功に役立つ学習環境を推進できます。それを円滑に進める方法の 1 つとして、チームの 1 人を指名して学んだことや共有したいことを話してもらうプレゼンテーションを定期的に行うことが挙げられます。新しい章のプレゼンテーションをする有志を、チームが言語に慣れるまで毎週募ることで、学習グループを活用できます。

チャンピオンを指名する

最後に、学習の取り組みをリードするチャンピオンを指名します。導入プロセスを開始する際には、この人物がチームリード(SME)の役割を果たすことができます。Kotlin に関連するすべての練習会にこの人物を含めることが重要です。チャンピオンとしては、すでに Kotlin に興味を持ち、ある程度の実践的知識を持っている人物が理想的です。

時間をかけて統合する

重要なのは、初めに時間をかけて、エコシステムのどの部分を最初に動かすべきかを戦略的に考えることです。これは多くの場合、主要アプリではなく組織内の 1 つのアプリを特定して行うのが効果的です。選択したアプリの移行について、状況がそれぞれ異なることを前提に、一般的なスタート ポイントをいくつか紹介します。

データモデル

データモデルは、たいていの場合、多くの状態情報といくつかのメソッドで構成されています。データモデルには、toString()equals()hashcode() などの一般的なメソッドがある場合もあります。通常、これらのメソッドはそれぞれ単独で移行および単体テストを容易に行うことができます。

たとえば、次のような Java のスニペットがあるとします。

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 + '\'' +
               '}';
   }
}

次に示すように、同等の Kotlin に翻訳できます。

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

このコードについては、現在のテストスイートに照らし合わせて単体テストを実施できます。ポイントは、一度に 1 つのモデルで小規模に始め、(ほとんどが行動ではなく状態である)クラスを移行させることです。移行中にテストを実施するのを忘れないでください。

テストを移行する

もう 1 つの開始方法としては、既存のテストを変換して、Kotlin での新しいテストの記述を開始する方法があります。この方法では、APK に同梱予定のコードを記述する前に、チームが言語に慣れるための時間を取ることができます。

ユーティリティ メソッドを拡張関数に移動する

静的ユーティリティ クラス(StringUtilsIntegerUtilsDateUtilsYourCustomTypeUtils など)を Kotlin 拡張関数として表現し、既存の Java コードベースで使用できます。

たとえば、いくつかのメソッドを持つ StringUtils クラスがあるとします。

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
   }

}

次の例のように、これらのメソッドはアプリの別の場所で使用される可能性があります。

...

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

...

Kotlin 拡張機能を使用すると、同じ Utils インターフェースを Java 呼び出し元に提供するとともに、拡大する Kotlin コードベースにさらに簡潔な API を提供することが可能です。

これを行うには、IDE によって提供される自動変換を使ってこの Utils クラスを Kotlin に変換することから始めてもよいでしょう。出力例は次のようになります。

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
   }

}

続いて次の例に示すように、クラスまたはオブジェクトの定義を削除し、この関数を適用する型を各関数名の前に付け、これを使って関数内の型を参照します。

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
}

最後に、次の例のように、ソースファイルの先頭に JvmName アノテーションを追加して、コンパイル済みの名前にアプリ内の他部分との互換性を持たせます。

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

最終的に次のようになります。

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

これらの関数は、各言語に見合った規則に従って Java や Kotlin を使用して呼び出せるようになります。

Kotlin

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

Java

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

移行を完了する

チームが Kotlin に慣れ、比較的小さな分野の移行が済んだら、フラグメント、アクティビティ、ViewModel オブジェクト、その他のビジネス ロジック関連クラスなど、より大きなコンポーネントへの取り組みに移ります。

考慮事項

Java に固有のスタイルがあるのと同じように、Kotlin にも、簡潔化に寄与する独自の慣用的スタイルがあります。ただし当初は、チームが作成する Kotlin コードが、置き換え対象の Java コードのように見えることもあるでしょう。 これは、チームが Kotlin の経験を積むことで変わっていきます。成功の鍵は、徐々に変えていくことです。

Kotlin コードベースが拡大していくと、次のようなことによって整合性の実現を図れるようになります。

一般的なコーディング標準

導入プロセス初期にコーディング規則の標準セットを定義しておく必要があります。適用可能な Kotlin スタイルガイドからそれることも可能です。

静的分析ツール

Android Lint などの静的分析ツールを使用して、コーディング標準セットをチームに適用します。サードパーティの Kotlin リンターである klint からも、Kotlin の追加ルールが提供されています。

継続的インテグレーション

必ず共通のコーディング標準に則り、Kotlin コードを十分にテストしてください。これを自動化ビルドプロセスの一環として行うと、これらの標準の一貫性と遵守を確保できます。

相互運用性

Kotlin はほとんどの部分において Java とシームレスに相互運用しますが、次の点に注意してください。

null 値許容

Kotlin は、コンパイル済みコードの null 値許容アノテーションを利用して、Kotlin 側での null 値許容を推測します。アノテーションが提供されない場合、Kotlin はデフォルトで、null 値許容型としても null 値非許容型としても扱えるプラットフォーム型になります。ただし、慎重に取り扱わないとランタイム NullPointerException 問題につながる可能性があります。

新機能を採用する

Kotlin は、多数の新しいライブラリと糖衣構文を提供してボイラープレートを減らし、開発速度の向上に寄与しています。ただし、コレクション関数コルーチンラムダなど、Kotlin の標準ライブラリ関数を使用するときには注意と秩序が必要です。

Kotlin での開発経験が少ない場合に遭遇しがちな落とし穴を示します。次のような Kotlin コードがあったとします。

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()
}

この例の目的は、nullableFoo が null でない場合に foo.baz()foo.zap() を実行し、それによって NullPointerException を回避することです。このコードは想定どおりに動作しますが、次の例に示すように、単純な null チェックやスマート キャストに比べると、読みやすさでは劣ります。

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
}

テスト

Kotlin では、クラスとその関数は、拡張に対してデフォルトで閉じられています。サブクラス化したいクラスと関数は明示的に開く必要があります。この動作は、言語設計上の判断として、継承よりも作成を促進する目的で選択されたものです。Kotlin には委任を通して動作を実装するサポートが組み込まれ、作成の簡略化に寄与しています。

この動作は、テスト中にインターフェースの実装や継承に依存して動作をオーバーライドするモック フレームワーク(Mockito など)で問題を引き起こします。単体テスト向けには、Mockito の Mock Maker Inline 機能の使用を有効化できます。これにより、最終的なクラスとメソッドをモックできます。 あるいは、All-Open コンパイラ プラグインを使用して、コンパイル プロセスの一貫としてテストする Kotlin クラスおよびそのメンバーを開くこともできます。このプラグインを使用する主なメリットは、単体テストとインストゥルメント化テストの両方に有効である点にあります。

詳細

Kotlin の使用に関する詳細については、次のリンクを参照してください。