JavaからKotlinに変換する7つのテクニック Kotlinらしさを生かした簡潔なコードに置き換えよう

既存のJavaコードをKotlinに変換する場面を想定し、より簡潔でKotlinらしいコードに置き換えるテクニックを、ヤフー株式会社でYahoo!ニュースアプリを開発する池田惇さんが解説します。開発現場にまだ多く残るJavaコードを必要に応じてKotlinへ置き換えることで、開発の負担を減らすことができます。

JavaからKotlinに変換する7つのテクニック Kotlinらしさを生かした簡潔なコードに置き換えよう

アプリエンジニアの池田惇@jun_ikdです。
これまでYahoo!ニュースや映像配信サービスGYAO!のAndroidアプリにKotlinを導入して、Javaからの置き換えを行ってきました。その経験などをもとに、既存のJavaコードを変換する際にどのように書けば、Kotlinの長所を生かすことができるかを紹介していきます

本記事のコードは下記の環境で制作しています。

  • Build #IC-183.5912.21, built on February 26, 2019
  • JRE: 1.8.0_152-release-1343-b28 x86_64
  • JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
  • macOS 10.14.3

Kotlinの利用拡大とJavaからの変換

Kotlinは、Java仮想マシン上で動作するプログラミング言語として2011年7月に発表され、Google I/O 2017でAndroidの開発言語として正式に採用されました。

1Kotlin Programming Language

サーバサイド/クライアントサイドのいずれもKotlinで開発できますが、特にAndroidアプリ開発においてはJavaに代わってもはやスタンダードになったと言えるのではないでしょうか。Javaと比較して短く簡潔に書けるという特徴から、勉強会のコード例などは今やほぼ全てKotlinで書かれていると思います。

GitHub社のブログ記事によると、Kotlinは2018年に最も利用が拡大したプログラミング言語とされています。

The State of the Octoverse: top programming languages of 2018 - The GitHub Blog

JavaからKotlinへの自動変換機能

Kotlin開発を始める方の多くは、既にJavaで書かれたアプリケーションを担当されており、JavaからKotlinへの置き換えを進めることが増えていくと思います。

統合開発環境であるIntelliJ IDEAやAndroidStudioには、次の画像のようにJavaからKotlinへの自動変換機能が備わっています(メニューバーの「Code」→「Convert Java File to Kotlin File」メニュー)

3

この自動変換はたいへん優秀なので、自動変換しただけで、動くKotlinコードが完成することもあります。しかし、言語の長所を生かしたKotlinらしいコードには、いま一歩及ばないことが多いと思います。

本記事では、JavaからKotlinに置き換える際のテクニックを紹介し、簡潔でメンテナンスしやすいコードを目指します。

Kotlinの特徴

実際にコードを紹介する前に、簡単にKotlinの特徴を見てみましょう。

Kotlinの公式サイトには、Kotlinを選ぶ理由として以下の4つが挙げられています。

簡潔さ
後述するdata classやラムダ式など、コードを簡潔に記述できる仕組みがあります。
安全性
null許容・非許容の型が区別されるため、想定外のNullPointerExceptionを防止できます。
var hoge: String
hoge = null // コンパイルエラー
Javaとの相互運用性
Java向けに開発されたライブラリが、ほぼ全て利用できます。もちろん、Java・Kotlin双方からコードを呼び出すことができます。
開発ツールとの親和性
KotlinはもともとJetBrains社で開発されていたことから、統合開発環境であるIntelliJ IDEAとその派生であるAndroidStudioでは、Kotlin開発のためのサポートが充実しています。

Kotlinについて詳しく学びたい方は、下記の書籍などを参考にされると良いと思います。

Javaからの変換テクニック集

ここからは具体的に、JavaからKotlinへ変換する際のテクニックを挙げていきます。

コードの記述方法に正解はありませんが、書き方を選択する際の目安にしていただけると良いと思います。

1. nullチェックを簡潔に書く

値が存在しないこと表すとき、Java・Kotlinのいずれでもnullを使います。

nullの変数を誤って参照するとNullPointerExceptionが発生してアプリケーションが停止してしまいます。これはJavaアプリケーションにおける最も多いクラッシュの一つと言えると思います。

これを防止するため、Javaでは@Nullableアノテーションを使って変数にnullが入ることを明示したり、if文を使ってチェックをするなど、多くの箇所で煩雑な記述を行う必要がありました。

Kotlinにおいてもチェック自体は必要なのですが、下記のパターンに応じてそれぞれ簡潔に記述することができます。

  • メソッドの引数がnullだったら処理を抜ける
  • 変数がnullでないときだけ処理をする

この2つを、まずはJavaで書いた例を見てみましょう。いずれのパターンもif文を使って書くことが一般的だと思います。

// メソッドの引数がNullだったら処理を抜ける
void someMethod(@Nullable String s) {
    if (s == null) return;
}

// 変数がNullで無いときだけ処理をする
if (hoge != null) {
    // 処理本体
}

Kotlinではそれぞれ、エルビス演算子?:とスコープ関数letを使って、このように記述できます。

// メソッドの引数がNullだったら処理を抜ける
fun someFunction(s: String?) {
    s ?: return
}

// 変数がNullで無いときだけ処理をする
hoge?.let {
    // 処理本体  
}

2. データを表すオブジェクトにはdata classを使う

Javaでデータを表すオブジェクトを作る場合は、下記のようになると思います。

public class SomeObject {

    private String mValue;

    SomeObject(String value) {
        mValue = value;
    }

    public String getValue() {
        return mValue;
    }

    @Override
    public boolean equals(Object o) {
        // 省略
    }

    @Override
    public int hashCode() {
        // 省略
    }

    @Override
    public String toString() {
        // 省略
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 省略
    }
}

この例のSomeObjectmValueというString型の値を1つ持っているだけです。しかし、同値性を表すためにequals()hashCode()、文字列として出力するためにはtoString()、複製のためにclone()を実装する必要があります。

同じコードをKotlinで書くと、次の1行になります。

data class SomeObject(val value: String)

値を得るためのgetterも書く必要はありませんし、equals()等のメソッドは自動的に実装済みになります(独自のロジックを使いたい場合は自分で実装します)

また、複製にはcopy()関数を使うことができます。既存のインスタンスから一部の値を変更しつつ新しいインスタンスを作ることが簡単にできます。

// copy()関数を使った複製
val someObject = SomeObject("hoge")
val newObject = someObject.copy("newValue")

3. 1つのファイルに複数のクラスを書ける

Javaでは、publicなクラスは1ファイルに一つしか記述できません(ファイル名=publicなクラス名になります)

Kotlinでは、1ファイルに複数のクラスを書くことができます。前述の軽量なdata classなどは1ファイルにまとめておくことで、開発時に見やすくすることができると思います。

// 数行のクラスなどは1ファイルに並べて書いたほうが使いやすいことがある

data class SomeObject(val value: String)

data class User(
  val id: String,
  val name: String,
  val age: Int
)

4. Listenerクラスは不要

ボタンのタップや非同期処理の完了などイベントを通知するため、JavaではListenerを使います。

下記の例では、JavaClassのコンストラクタでSomeListenerを渡しています。検知したいイベントが起きたときに、onEvent()メソッドが呼び出されます。

public class JavaClass {

    interface SomeListener {
        void onEvent();
    }

    private SomeListener mListener;

    JavaClass(SomeListener listener) {
        mListener = listener;
    }

    void doEvent() {
        mListener.onEvent();
    }
}

Javaは、関数が第一級オブジェクトではありません(変数に格納できず、引数・戻り値として受け渡しできない)。そのため、上記のようにListenerクラスを渡す必要がありました。

しかし、Kotlinの関数は第一級オブジェクトなので、受け渡しをすることができます。同様のコードをKotlinで書くとこうなります。

class KotlinClass(private val callBack: () -> Unit) {

    fun doEvent() {
        callBack()
    }
}

引数なし、戻り値Unit型(何も返さない)の関数callBackを渡しており、JavaのようにInterfaceを定義する必要はありません。

5. objectでシングルトンを簡単に使える

エンジニアHubに会員登録すると
続きをお読みいただけます(無料)。
登録のメリット
  • すべての過去記事を読める
  • 過去のウェビナー動画を
    視聴できる
  • 企業やエージェントから
    スカウトが届く