Android公式言語 Kotlinとはどんな言語でどんなメリットがあるのか?!

久々にAndroid Developersに行ってみたら、何やらKotlinという言語が公式言語に追加されたという記事を発見しました。
今年の2017年05月18日にGoogleがKotlinという言語を公式言語に追加すると発表したみたいです。

AppleがiOSの公式言語としてSwiftを発表したのが約3年前の2014年。
そんなiOSに対抗するかのようなこの発表は、遅まきながら僕の心に衝撃が走りました。


ではこのKotlinという言語はどんな言語なのか? どんなメリット、利点を開発者にもたらしてくれるのか? どんなことができるのか? 気になったので調べてみました。



前半はKotlinの概要、GoogleがAndroidにKotlinを公式言語として採用した経緯などをまとめていますので、具体的なメリットなどについて確認したい方はここをクリックしてください。


参考にしたページは以下になります。
Kotlin and Android
Using Kotlin for Android Development





そもそもKotlinって何?

KotlinはJVM(Java仮想マシン)上で実行される静的型付けのオブジェクト指向言語です。
JavaScriptのソースコードにコンパイルしたり、LLVMコンパイラインフラストラクチャを使用することもできます。


構文自体はJavaと互換性はありませんが、Javaコードと相互運用できるよう設計されています。
なので、KotlinからJavaのコードを呼び出せますし、JavaからKotlinのコードを呼び出すこともできます。

Javaとの構文の違いに関してですが、恐らく、Swiftのような近代的な言語の構文に合わせたのではないかと予想しています。


また、KotlinはJavaのコレクションフレームワークなどの既存のJavaクラスライブラリに依存するようです。

ちなみに静的型付け言語とは何かというと、Java、C、C#のようにコンパイル時に型の整合性を確認する言語のことです。


この言語の主な開発は、JetBrains社のプログラマーチームによって行われています。
JetBrains社はロシアのサンクトペテルブルクにあるそうです。

Kotlinの名前の由来は、サンクトペテルブルクの近くにあるKotlinという島の名前から来ているみたいです。

KotlinがAndroidで採用された経緯

実はKotlinは、Javaとの相互運用が可能なことから、Androidの公式言語に採用される前から多くの開発者によって使用されていたようです。

そしてGoogleは、KotlinのNull安全性やオブジェクトのイミュータビリティ(変更不可能性)、何よりも既存のAndroidライブラリと相互運用可能な点に注目していたようです。

そういう過程から、GoogleはKotlinをAndroidの公式言語に追加したようです。

既存のAndroidアプリにKotlinを組み込むこと

Kotlinは、Android上でJava、C++と並行して動作します。
なので、既存のコード、既存のAndroidライブラリを使用しながら、Kotlinのコードを追加することができます。
段階的に、既存のJavaコードをKotlinに置き換えていくこともできます。

Kotlinの概要でも述べたとおり、KotlinはJavaのコードを呼び出すことができますし、逆にJavaからKotlinのコードを呼び出すことができるので、簡単に既存のプロジェクトにKotlinを組み込むことができます。

Android StudioでのKotlinのサポートについて

現代のアプリ開発において、統合開発環境は切っても切り離せないくらい重要なものとなっています。
なので、Androidの開発で使用するAndroid StudioでのKotlinのサポートも重要になってきますよね。


Android StudioがJetBrainsの統合開発環境、IntelliJ IDEAを基に構築されていることをご存知の方は多いかと思います。

Kotlinの概要を読んだ方はお気づきだと思いますが、Kotlinを開発した会社もJetBrainsです。
そのJetBrainsはIntelliJ IDEAでKotlinが申し分なく動作するよう何年間も開発を続けています。
なので、IntelliJ IDEAを基にしたAndroid StudioでもKotlinのサポートを全て受け継いでいます。


Android Studio 3.0以降では、KotlinのためのツールがAndroid Studioに直接同梱されているようなので、これからKotlinを使用して開発する方はAndroid Studio 3.0以降を使用することをおすすめします。

KotlinとAndroidの思想について

Kotlinは主にApache License 2.0のオープンソースプロジェクトとして開発されています。
同じようにAndroidも開発されています。

そういう思想の一致からも、Androidの公式言語として採用された理由になっているのではないかと僕は考えています。

KotlinをAndroidの公式言語にしちゃったけど、今後GoogleはJavaやC++のサポートをしていかないの?

この記事をここまで読んだ皆さんは、今まで慣れ親しんで使用してきたJavaが今後Androidでサポートされなくなるのではないか? と心配になったりしますよね。

安心してください!
Googleは、Kotlinを公式言語に追加する一方で、既存のモノの拡張もしっかり進めていると述べています。


例えば、Android 8.0 Oreoでは、より多くのJava 8.0ライブラリのためのサポートを追加しているようです。
Android Studio 3.0では、Java 8.0の言語機能がjavacコンパイラで直接サポートされるようになもっているようです。


更に、C++上では、ネイティブな体験を完全にカバーするために、パフォーマンスプロファイリングツールやAPKデバッグツールなどへの投資も継続して行っているようです。
更に更に、C++ライブラリサポートを大幅に改善し、古いAPIを使用する場合でも、最新の機能にアクセスできることを保証するためにNDKの更新もしているようです。


このことから、今後もKotlinと並行して、その他の言語のサポートも継続していくようです。


ただし、C++は別にして(ネイティブな言語なため)、Javaは進化しているといっても、歴史ある古い言語になりつつあり、まだまだ現役ではありますが、いつAndroidからそのサポートが切られるかわかりません。

個人的な意見としては、近代的な機能を備えたKotlinを使用したAndroidでの開発方法を把握しておき、いつでもJavaから移行できるようにしておくのがよいかと思っています。
近代的な言語の構文の勉強にもなりますしね。

僕も少しずつKotlinのことを学んでいこうと思っています。

KotlinをAndroidアプリ開発に使用するメリット、利点

それではKotlinを使用したAndroid開発のメリット、利点は何かを見ていきましょう!

KotlinとAndroidとの互換性がすごい

KotlinはJDK 6(Java Development Kit)と完全に互換性があるため、古いAndroid端末でもKotlinで作成したアプリを問題なく実行することができます。

なので、既存のプロジェクトに気軽にKotlinのコードを追加することができますし、JavaでコーディングされていたソースコードをKotlinに置き換えることもできます。

もちろん、既存のAndroidのライブラリもJavaのライブラリもKotlinで使用することもできます。


Kotlinは近代的で効率的な言語機能を備えた言語です。
それを気軽に追加、Javaからの置き換えができるのは大きなメリットだと言えます。


また、Android Studioでは、Kotlinのツールが完璧にサポートされています。
そしてAndroidのビルド体系にも互換性があります。

Kotlinの性能はJavaと同じ速さ、時にはそれ以上

Kotlinで開発されたアプリは、同等のJavaアプリと同じ速度で実行することができます。
つまり、JavaコードをそのままKotlinに移植した場合、パフォーマンスが落ちることなく、同じように実行することができます。


Kotlinはインライン関数をサポートしています。
ラムダ式でインライン関数を使用するコードは、Javaで書かれた同じコードよりたいてい速く実行されるようです。


例えば以下のコードはKotlinのほうが速く実行されます。

// Kotlinのコード
fun printMesssages(messages: List<String>) {
    messages.forEach { println(it) }
}
// Javaのコード
public void printMesssages(List<String> messages) {
    messages.forEach(it -> System.out.println(it));
}

なぜラムダ式を使うとJavaのほうが遅くなるのか?

Javaでラムダ式が使用されると以下のような動作をします。
①ラムダ式に指定した処理を内包したメソッドとコンストラクタを持つ無名クラスが作成されます。
②forEachが呼ばれるたびにその無名クラスがインスタンス化され、インスタンス化したクラスのメソッドにアクセスし、処理を実行します。

そのため、ラムダ式をインライン展開できるKotlinより遅くなるということになります。


ではKotlinでインラインのラムダ式を使用するとどうなるのか?

Kotlinでインラインのラムダ式を使用すると以下のような動作をします。
①ラムダ式に指定した処理はクラス化されることなく、呼び出し元の関数にインライン展開されます。
②呼び出し元の関数のバイトコードに指定した処理が直接挿入されます。

Javaとは異なりクラスをインスタンス化したりしない分、動作が速くなるということです。


また、Kotlinではインライン関数を使用することで、Javaのラムダ式内ではサポートされていないreturnを行うこともできます。

以下のJavaのコードは、実行した場合、ラムダ式内のreturnが実行されてもメソッドが終了せず、必ずfalseが返ります。
つまり、0がリストの中に存在してもtrueが返ることはありません。

// Javaのコード
public boolean hasZeros(List<Integer> ints) {
    ints.forEach(it -> {
        if (it == 0) return true;
    });
    return false;
}



しかし、Kotlinでは、インライン展開したラムダ式内のreturnが実行されると関数が終了するようになっているので、意図したとおりにBooleanが返ります。

// Kotlinのコード
fun hasZeros(ints: List<Int>): Boolean {
    ints.forEach {
        if (it == 0) return true
    }
    return false
}

なぜ、kotlinではインラインのラムダ式でreturnが実行されるのかというと、forEach内のバイトコードに指定した処理が直接挿入されるためです。
なのでJavaとは異なり、意図したとおりにreturnが実行されます。


Kotlinの標準ライブラリにあるラムダ式を引数とした関数は、ほとんどインライン展開されるように定義されていますので、上記のような直感的かつシンプルなコードを書くことができます。

これはKotlinを使用する大きなメリットになります。


ですが、2017年10月09日時点のKorlinのバージョンでは、インライン展開されたラムダ式内でのbreak、continueは利用できないようです。
注意してください。

ちなみに、インライン展開していないラムダ式でreturnを使用するとJavaとは異なり、Kotlinではコンパイルエラーになります。


インライン関数についての詳細はここにあります。
(英文のため、後に記事にまとめる予定です)

KotlinはNull安全に設計されているので、もうヌルポに怯えなくていい

まずはNull安全ではない言語の話を少しします。

Null安全ではないプログラミング言語では、実行時にnullが発生する可能性がある場合、nullチェックなどをして発生を回避する必要があります。

たびたび、僕たちはきちんとnullチェックをしたと『思っていて』も、JavaでいうNullPointerExceptionの発生に悩まされます。

複雑ではない箇所でのnullの参照なら簡単に解決することができるので問題ないのですが、時に、複雑な処理の過程でそれまでnullでなかったオブジェクトが、意図してなのか意図せずなのか、nullに変化することが起こったりします。

そしてnullに変化したオブジェクトを参照しエラーが発生することにより、そのバグを修正するために思いがけない時間がかかることがままあります。


ですがそういう悩みもKotlinではほぼ解消します。
KotlinはNull安全に設計されているからです。

例えば以下のようにnullを変数に代入しようとするとコンパイルエラーになります。

var nonNullString: String = "abc"
nonNullString = null //ここでコンパイルエラー

Kotlinにはnullの使用を許可するかどうかを明示的に記述する必要があるように言語の構文で決められています。

上記の処理ではnullの使用を許可するための記述がされていないにも関わらず、nullの参照を代入しようとしているため、コンパイルの構文チェックで引っかかり、コンパイルエラーになります。


もしnullを代入したい場合、KotlinではNull可能な変数として以下のように宣言する必要があります。

var nullableString: String? = "abc"
nullableString = null //コンパイルエラーにならない

KotlinではNull可能なオブジェクトを使用したい場合、nullを使用したい型に?を付与をすることで使用できます。
String?と明示しないかぎりnullを代入できないため、それだけでもかなりのNullPointerExceptionを回避することができます。


そしてnull可能なオブジェクトとして宣言した変数を参照する場合、必ずnullチェックをするように言語の構文で決められています。

以下の場合、コンパイルエラーになります。

val length = nullableString.length // 変数 nullableStringはnullの可能性があるため、コンパイルエラーになる

もしこのNull可能な変数nullableStringを参照したい場合、以下のようにnullかどうかの確認を行う必要があります。

val length = if (nullableString != null) nullableString.length else -1



このように、KotlinではNull可能なオブジェクトの参照に対して厳しく構文を定めており、コンパイル時点でNull参照に関する警告をコンパイルエラーとして発生させるため、Null安全が保証されています。


ですが、完全にNull参照のエラーから解放されるわけではなく、Kotlinでも、ある特定の状況ではNullPointerExceptionが発生してしまうケースがあります。

Null安全についての詳細をまとめました。
以下を参考にしてください。

KotlinのNull安全ってどう安全なの?! 検証も交えてまとめてみた!



Kotlinはオブジェクトのイミュータビリティ(変更不可能性)、ミュータビリティ(変更可能性)を意識しやすい言語仕様になっている

この話に入る前に、イミュータビリティ、ミュータビリティってなんやねん! と思われる方もいるかと思います。
僕もKotlinに触れ始めて知った概念です。


かいつまんで説明すると、イミュータビリティとはオブジェクトの変更が不可能であることを保証するという概念になります。

逆にミュータビリティとはオブジェクトに対して変更が可能であることを示す概念になります。


変更不可能なオブジェクトのことをイミュータブル(変更不可能)オブジェクトと呼んだり、変更可能なオブジェクトのことをミュータブル(変更可能)オブジェクトと呼んだりします。


Kotlinでは言語仕様でこのイミュータビリティとミュータビリティを意識しやすいようになっています。

例えばKotlinで変数を宣言する時、以下のように変数が変更不可能か変更可能かを必ず示さなければならないようになっています。

val immutableInt: Int = 1234
var muttableInt: Int = 5678

valで宣言した変数は値を代入後、二度と変更することはできません。
varで宣言した変数はいつでも値を変更することができます。


valで宣言した変数に再度値を代入しようとするとコンパイルエラーになります。
そのため、一度イミュータブル(変更不可能)として宣言したオブジェクトは変更されていないことが保証されます。


このイミュータビリティはプログラムを作成する上でかなり役に立つモノとなります。


例えば、データベースから参照用としてデータを取得し、画面上にそのままデータを表示したいとします。

Javaでこの処理を行った場合、ArrayListなどで値を取得するかと思いますが、取得後、どこかの処理でこのArrayListの内容が書き換えられてしまう可能性があります。
仮に値を書き換えられてしまった場合、そのデータはデータベースに存在する正しいデータではなく、何者かによってデータが改ざんされた間違いを伴ったデータとなってしまいます。


Kotlinではイミュータブルを標準でサポートしているため、間違えて書き換えてしまいたくない場合、イミュータブルな変数、イミュータブルなオブジェクトを使用することにより、この問題を解決できます。

Kotlinでは上記のような処理を行いたい場合、Listと、valで宣言されたプロパティのみを持つクラスを使用することにより、変更不可能を保証するオブジェクトとして使用することができます。

// valで宣言されたプロパティのみを持つクラス
data class Person(val name: String, val age: Int)

// valで宣言されたクラスのリストを画面に出力する
fun printMyData(myDatabase: MyDatabase) {
    val myReferredList: List<Person> = myDatabase.getMyData()

    // myReferredList.add(something) ←add関数がないため、追加できない

    // person: Person = myReferredList.get(0)
    // person.name = "someone" ←valのプロパティのため変更できない

    // 変更されていないことが保証された状態で参照できる
    myReferredList.forEach {
        println("name = ${it.name}")
        println("age = ${it.age}")
    }
}

このように、できる限りイミュータブルなオブジェクトを使用する方がバグが発生しにくいと言われています。
Kotlinを使用することで、思いがけないバグの問題から解放されます。


ですが、取得したデータを加工して表示したい場合もあると思います。
その場合、MutableListとvarで宣言されたプロパティを持つクラスを使用することでミュータブル(変更可能)なオブジェクトとして使用できます。


このようにイミュータブルとミュータブルの使い分けをさせるようなKotlinの言語仕様により、開発時に使用するオブジェクトのイミュータビリティ(変更不可能性)やミュータビリティ(変更可能性)を意識させられ、より効率のよい設計ができるようになっています。
これは明らかな利点だと言えます。


イミュータブル、ミュータブルについてより詳しくまとめて記事にしました。
よければ合わせてご参照ください。

Kotlinのイミュータブル、ミュータブルとはなんなのか? どんなメリットがあるのか?



Kotlinではif文を式として使える

まずはJavaのif文に関するコードを見てください。

// Javaのコード
String name = "Anonymous";
if (user.isLoggedIn) {
    name = user.name;
}

よく見る処理ですよね。
ログインユーザーのデフォルトの名前を最初に代入しておき、ユーザーがログイン状態なら、ログイン状態のユーザーの名前を代入する、という処理になります。


上記の処理をKotlinで書くと以下のようになります。

val name: String = if (user.isLoggedIn) user.name else "Anonymous"

このように、たった1行で書けてしまいます。
些細なことですが、このようにif文を変数への代入式として使用でき、行数を減らすことは大きなメリットとなります。

なぜならば、ソースコードの行数が増えることにより、可読性やメンテナンス性が下がることがあるからです。


1行に詰め込みすぎてテクニカルな書き方になってしまうと可読性やメンテナンス性が落ちてしまいますが、上記のような簡単で理解しやすい処理は、どんどん使っていくべきだと思います。



KotlinとJavaとの間では100%の相互運用が保証されている

KotlinはJavaと100%の相互運用が保証されています。
なので、Kotlinのプログラム内で既存の全てのAndroidライブラリを使用することが可能となっています。


Javaとの相互運用が保証されているため、Annotation Processing APIも使用可能となっています。
そのため、データバインディングとDaggerも動作します。

と、公式ページに記載されていたのですが、Annotation Processing API、データバインディング、Daggerについての知識がないため、僕としては感動が少ないです(笑
どのようなものか気になるため、後日、Annotation Processing API、データバインディング、Daggerについて調べてみたいと思います。

データバインディングはC#のWPF的なものなのでしょうか?

活用できるプログラムが豊富

Kotlinは活用できるプログラムが豊富な割に、とてもコンパクトにまとまったランタイムライブラリのようです。
実際にAndroidアプリにKotlinのランタイムを追加しても、apkファイルには100kbに満たないサイズが追加されるだけのようです。
メソッドも数百ほどが追加されるのみとなっているようです。

なので、Kotlinに手を出してみたいけど、アプリの容量が増えるのはやだなって方も安心して追加できそうですよね。

加えて、ProGuardという難読化ツールを使用することで容量を更に減らすことが可能みたいです。

効率的なコンパイルにより、コンパイル時間がJavaと同じかそれ以上

Kotlinは効率的なインクリメンタルコンパイルをサポートしているため、Javaでコンパイルを行うのと同じかそれ以上の速さみたいです。
その代わり、クリーンビルド時にいくつかの追加のオーバーヘッドが発生するみたいです。
ですが、Androidアプリ開発時にクリーンビルドすることも頻繁にないと思いますので、あまり気にならないと僕は思っています。

むしろ、コード修正してビルドするほうが断然多いので、クリーンビルドのオーバーヘッドを差し引いてもお釣りがくるくらいでしょう。


インクリメンタルコンパイルって何なの? と思われた方もいるかと思います。
というより僕が思いました(笑

なのでインクリメンタルコンパイルについて調べました。
調べたところによると、インクリメンタルコンパイルとはプログラムに変更のあった箇所のみをコンパイルする仕組みのようです。
全てのソースファイルに対してビルドが行われない分、コンパイル時間の短縮につながるということですね。
調べた限りでは、Gradleを使用した場合のみ有効になるようです。


英文ですが、KotlinとJavaのコンパイル速度の対決についての記事があります。
面白そうなのでいつかこの記事についてまとめてみたいなと思っています。

JavaからKotlinに変換するツールがあるので、ソースを見比べることで習得速度を高められる

Kotlinのプラグインに含まれるJavaからKotlinへの変換ツールを使用することで、Javaで書いていたコードが、どのようにKotlinのコードに変換されるのかを比較することで、Kotlinがどのようなモノかを掴むきっかけになります。


JavaからKotlinへの変換ツールはAndroid Studio内で使用することができます。
別の記事で使い方を紹介したいと思っています。

Kotlinと連携したAndroid開発のためのツールが便利

Kotlinは標準の言語機能を超えたAndroid開発のための一連のツールを提供しています。
どのようなツールがあるのかを見ていきましょう。

Kotlin Android Extensions

なんとこのツールを使用することで、ソースコードにあるfindViewById()の呼び出しの代わりに、コンパイラで自動生成されたプロパティに置き換えられるようになります。

例えば、以下のように置き換えられるようになります。

class MyActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textView.setText("Hello, world!")
        // val textView = findViewById(R.id.textView) as TextView ←を行わなず、そのままプロパティとして使える
    }
}

直感的なコードになるので記述するのが楽になりそうですね。
欠点としては、こういうツール系はAPIの仕様を後追いするような形で作成されてしまうので、findViewByIdの仕様が変更された場合に対処しづらいというリスクがあります。

悩みどころですね。


ちなみにこのツールはGradleに使用することができるコンパイラ拡張です。

Anko

AnkoはAndroid APIをKotlinで使いやすくラッパーしたモノが集められたライブラリです。


Ankoを使うとActivity呼び出しのIntent処理のコード記述量が減らせたりします。

例えば、新しいActivityに、指定した値を渡して呼び出したい場合、以下のようにIntentを使いますよね。

val intent = Intent(this, NewActivity::class.java)
intent.putExtra("id", 5)
intent.setFlag(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(intent)



Activityを呼び出すいつもの流れなので見慣れた感がありますが、よく考えるとActivityを起動するまでに4行も書いているんですよね。
必要だとはいえ、ちりも積もればなんとやらで、コード量は増えていってしまいます。
そこでAnkoは上記の処理をより簡単に、より可読性よく記述できる機能を提供しています。

startActivity(intentFor<NewActivity>("id" to 5).singleTop())

な、なんと4行も費やしていた処理をたった1行で表現できてしまいました!
これでActivityの呼び出しが捗りますね。

ちなみにAnkoにはIntent以外に、Dialog、Toastなどなどの呼び出しを簡略化するための機能が提供されています。


また、DSL(Domain Specific Language/ドメイン固有言語)を使用したレイアウトの記述のサポートも提供しています。
以下のように、Listenerの定義を含めた記述ができます。

verticalLayout {
    val name = editText()
    button("Say Hello") {
        onClick { toast("Hello, ${name.text}!") }
    }
}

簡単に記述できるだけでなく、onClickにはsuspendラムダ式(中断ラムダ式)を使用することができ、非同期のコードをListenerの定義に書くこともできるようです。

suspendラムダ式についての詳細はここにあります。
(英文のため、後に記事にまとめる予定です)

また、このプラグインを使用することで、DSLで記述したコードをAndroid Studio上でプレビューできるようにもなるようです。


紹介したこのAnkoの機能はほんの一部で、まだまだ便利な機能があります。

このライブラリを使用できるだけでも、KotlinでAndroidアプリの開発をする価値がありそうですよね!


Ankoの機能の詳細についてはいくつかの記事に分けて詳しく説明していこうと思っています。

Kotlinを採用する流れは着々と来ている

Kotlinの公式ページに、いくつかのKotlinを採用したアプリの事例が記載されていました。
以下に記載します。


Pinterestというアプリをご存知の方も多いと思いますが、Kotlinが導入されているようです。


Basecamp 3というアプリでは、なんと全てのコードがKotlinで記述されているようです。
実際にKotlinを使用した開発者は開発に大きな違いを感じ、作業品質と作業速度に大きな改善が見られたようです。

こんな話を聞くとますますKotlinを使ってアプリの開発をしたくなりますよね。


Keepsafeのアプリロックは、全てのプログラムをKotlinに移植したようです。
移植したことにより、ソースの30%の削減とメソッド数の10%の削減に成功しているようです。

Kotlinをうまく使うことにより、効率的な設計ができるみたいですね。

Kotlinに移植したことについての記事がここにあります。
(英文です。後に記事にまとめるかもしれません)


このように、続々と大手企業のアプリでKotlinが採用されてきているようですね。
Kotlinは今後、iOSでいうSwiftのような存在になっていくのか、要注目ですね!

まとめ

今回、Androidの公式言語になったKotlinがどのような言語かをまとめ、近代的な言語の機能、思想の一部分に触れました。
JavaやC#とはまた違った言語機能、思想で、より近代的な言語だと感じ、そして時代の流れを感じました。

そのような先進的な言語Kotlinがどのように成長していくのか、今後も注目していきたいと思います。
そして今後、iOSのSwiftのようなポジションになっていくのかというところにも注目していきたいと思います。




コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です