KotlinでAndroid開発

この記事は [株式会社ソニックムーブ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/sonicmoov) 23日目の記事です。
株式会社ソニックムーブ
https://www.sonicmoov.com
島根事業所のFacebook
https://www.facebook.com/sonicmoov.shimane

# はじめに

先日島根県松江市で行われた「[フロントエンド勉強会 in 山陰#03](https://front-end.connpass.com/event/71553/)」イベントに参加し、KotlinでAndroid開発について発表をさせていただきました。発表の資料も公開したので、ぜひご覧ください。[Kotlin+Android](https://speakerdeck.com/paulbarre/kotlin-plus-android)
はじめてのブログの記事ですが、発表した内容をこちらも説明したいと思います。

![IMG_0174.JPG](https://qiita-image-store.s3.amazonaws.com/0/163874/91e90a37-4d70-7e69-161d-762e08dc0e3b.jpeg)

# Kotlin

KotlinはJetbrains社が開発しているオブジェクト指向プログラミング言語です。ちなみに、Android StudioがベースしているIntelliJ IDEAのIDEもJetbrains社が開発しています。
Kotlinの特徴をいくつかまとめます：

* 関数型と静的方付けの言語である
* Apache 2.0ライセンスに基づいてオープンソースのプロジェクトである（[Github](https://github.com/JetBrains/kotlin)）
* Java仮装マシン（JVM）上で動く
* Javaとの互換性は100%

ちなみに、Kotlinを開発しているチームがロシアにいて、コトリンは[ロシアにある島](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%88%E3%83%AA%E3%83%B3%E5%B3%B6)の名前です。

**履歴**

今までのKotlinの大きいマイルストーンを下記にまとめます。

* 2010年：Kotlinの開発開始
* 2011年7月20日：Jetbrains社がKotlinを開発することを発表しました
* 2012年2月14日：Apache 2.0ライセンスに基づいてオープンソース化になりました
* 2016年2月15日：Kotlin v1.0リリース
* 2017年5月17日：Google I/OでAndroidアプリ開発に当たって第一級言語の一つ選定

**Android世界**

KotlinでAndroid開発ができることになってから、Kotlinで開発したアプリがどんどん増えていますね。

![apps_made_with_kotlin.png](https://qiita-image-store.s3.amazonaws.com/0/163874/1c315017-73db-3b7c-bf32-cbf45568b510.png)
参考：Androidデベロッパー [Kotlin and Android](https://developer.android.com/kotlin/index.html)

`Slack`、`Reddit`など、最近の人気のアプリも入っています。

# Kotlinの機能

当記事ではKotlinの入門の話をするつもりではありませんが、一応自分が気に入った機能を紹介します。

## 基本の関数
例として加算する関数を書きます。

```kotlin:Kotlin
fun add(x: Int, y: Int): Int {
    return x + y
}
```
Kotlinではもし関数中に一つだけのオペレーションがあれば、括弧を外しても問題ないです。

```kotlin:Kotlin
fun add(x: Int, y: Int): Int = x + y
```

また、型推論があるので、結果の型が決まってるから、下記のように実装もできます。

```kotlin:Kotlin
fun add(x: Int, y: Int) = x + y
```

Java、Cなどと比べると、コードの量がかなり減りますね。

## Nullable

Javaエンジニアがよく知っている`NullPointerException`の例外を予防するため、Kotlinが`Null安全（Null safety）`用のNullableを提供します。

Javaの場合下記のコードで例外が発生します。

```java:Java
MyClass ins = null;
ins.toString();
```

例外が発生しないためKotlinでNullableを使えます。Nullable型は`null`になる可能性の変数の型です。クエスチョンマーク`?`で宣言します。

```kotlin:Kotlin
var ins: MyClass? = null
ins?.toString()
```

毎回`ins`のメソッドを参考する時に、`ins?.`で`null`かどうかを確認します。

プログラムをコンパイルする時にnullのチェックをしていない場合はコンパイルエラーが発生しますので、チェックの漏れがこれでなくなります。

```kotlin:Kotlin
val a: MyClass = null //コンパイルエラー
val b: MyClass? = null
b.toString() //コンパイルエラー
```

## when

Kotlinの`when`はJavaやCなどの`switch`代わりのキーワードです。

```kotlin:Kotlin
fun cases(obj: Any) {
    when (obj) {
        1 -> println("One")
        "Hello" -> println("Greeting")
        is Long -> println("Long")
        !is String -> println("Not a string")
        else -> println("Unknown")
    }
}
```

`when`の特徴は他にもあります：

* `when`が値を戻す
* 戻り値は自動でキャストされます
* 引数は必須ではありません

上記の３点を含めて、下記のように実装ができます。

```kotlin:Kotlin
val res = when {
    x in 1..5 -> "X between 1 and 5"
    s.contains("Hello") -> "Greeting"
    else -> "Unknown"
}
```

## 拡張関数

既存のクラスを拡張したい際に、拡張関数という機能を使えます。
例として`MutableList`クラスに２つの持っているエレメントを取り替えたい時、下記のように実装ができます。

```kotlin:Kotlin
fun<T> MutableList<T>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}
```

## 拡張プロパティ

関数と同様に既存のクラスのプロパティの宣言も可能です。

```kotlin:Kotlin
val<T> List<T>.lastIndex: Int
    get() = size - 1
```

# Androidでどうなる？

KotlinでAndroid開発する場合、Javaの場合と比べて説明します。

## シンプル

まずJavaよりシンプルに実装が出来ます。２つのパターンを説明します。

**データクラス**

Javaの場合モデルクラスを開発する際は、`get`や`set`や`toString`などの関数も含めて実装をしないといけません。

```java:Java
public class Person {
    
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
```

Kotlinの場合は`data class`を使います。

```kotlin:Kotlin
data class Person(val name: String, val age: Int)
```

上記のコードで下記のメソッドが自動で定義されます。

* get
* set
* `toString`
* `hashCode`
* `equals`

```kotlin:Kotlin
val p1 = Person("Michel", 30)
val p2 = Person("Michel", 30)
println(p1.equals(p2)) // true
```

**ラムダ式**

例として、画面のレイアウトのコンポーネントに`onClick`リスナーを設定する際のコードを説明します。

```java:Java
view.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        doSomething();
    }
});
```

上記のコードをKotlinへ単純に変換すると下記のコードになります。

```kotlin:Kotlin
view.setOnClickListener(object: OnClickListener {
    override fun onClick(v: View) {
        doSomething()
    }
})
```

`OnClickListener`というインターフェースが一つのメッソッドだけを定義していますので、下記のようにシンプルになります。

```kotlin:Kotlin
view.setOnClickListener({ view -> doSomething() })
```

`view`の引数を使っていないので、下記のように実装できます。

```kotlin:Kotlin
view.setOnClickListener({ doSomething() })
```

`setOnClickListener`の引数は一つしかないため、ラムダ式で書いても大丈夫です。

```kotlin:Kotlin
view.setOnClickListener { doSomething() }
```

## 便利なツール

自分が使ったツールを紹介します。

* Kotlin Android Extensions
* Ankoライブラリ

### Kotlin Android extensions

このプラグインのおかげで、Androidエンジニアのよく知ってる`findViewById`が不要になります。
例として、`TextView`の`text`の設定の実装を説明します。

```xml:activity_main.xml
<TextView
    android:id="@+id/hello" />
```

上記の`TextView`にアクセスするため、Javaでビューの`findViewById`を呼んで、キャストをしないといけません。

```java:Java
TextView view = (TextView) findViewById(R.id.hello);
view.setText("Hello!");
```

Kotlin Android Extensionsを使うことでJavaの場合よりシンプルに書くことができます。

```koltin:Kotlin
import kotlinx.android.synthetic.<layout>.*

...

hello.text = "Hello!"
```

インポートを追加した上で全ての`id`を持ってるレイアウトのコンポーネントにアクセスが出来ます。キャストも不要で直接`TextView`の関数を呼べます。

参考：Kotlin [Kotlin Android Extensions](https://kotlinlang.org/docs/tutorials/android-plugin.html#using-kotlin-android-extensions)

### Ankoライブラリ

KotlinでAndroid開発しますと、ネット上で検索する時にAnkoがよく出てくるライブラリの名前です。
このライブラリではいくつかの機能が提供されていますが、２つの気に入った機能を説明します。

参考：Anko [Github](https://github.com/Kotlin/anko)

**Anko layouts（DSL）**

Anko Layoutsで直接Kotlinのコード内にレイアウトの定義ができます。

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

個人の意見ですが、レイアウト（ビュー）とアクティビティやフラグメント（コントローラー）をできるだけに別けた方がいいと思いますので、XMLでのレイアウトの方がいいと思います。ただし、場合によって便利の時もあるかと思っております。

**Anko Intent**

またシンプルの話になりますが、Ankoを使うと`Intent`の宣言がかなりしやすくなります。
Kotlinでパラメーターを持つ`Intent`で`Activity`をスタートするコードは下記になります。

```kotlin:Kotlin
val intent = Intent(this, SomeOtherActivity::class.java)
intent.putExtra("id", 5)
intent.setFlag(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(intent)
```

AnkoのIntentを使えば、下記のコードが出来ます。

```kotlin:Kotlin
startActivity(intentFor<SomeOtherActivity>("id" to 5).singleTop())
```

## JavaからKotlinへ

KotlinはJavaとの互換性が100%で、プロジェクト内に両言語のファイルがあっても可能です。それでは、既存のJavaアプリをKotlinに変換することが一歩一歩でできます。
あと、Android Studioが便利なサポートも提供しています。例としてStackoverflowとかでコードをコピーして、Android Studioにペーストすると、下記のダイアログが出てきます。

<img width="517" alt="スクリーンショット 2017-12-20 10.18.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/163874/d7c53b16-b78e-5a89-9c1d-ac8065c559dc.png">

*Last but not least*、Android StudioでJavaのファイルを開いたままで`Code`メニューから`Convert Java File to Kotlin File`オプションからIDEに任せて言語の変換が可能です。

![as_java_to_kotlin.png](https://qiita-image-store.s3.amazonaws.com/0/163874/8afcd68e-aa74-b8ff-a72b-4425e8d259a1.png)

# 気になったところ

## Kotlin

**条件演算子がない**
なかなか条件演算子を使うことになれたら、ないとちょっと面倒だと思います。一応Kotlinで下記の実装ができます。

```kotlin:Kotlin
val res = if (i > 0) "positive" else "negative"
```

**Swiftみたいのguard letがない**
Swiftで実装しますと、`guard let`がかなり便利だと思っています。Kotlinではそういうものはありませんので、ちょっと残念だと思います。

```swift:Swift
guard let obj = obj {
    return
}
doSomething()
```

一応`guard let`みたいなコードが出来ますが、あまり宜しくないと感じています。

```kotlin:Kotlin
obj?.let {
    doSomething()
} ?: run {
    return
}
```

**`let`はSwiftと違う**
Swift言語の場合、 同一の`if`条件でいくつかの`let`を使って条件を確認できます。Kotlinで同じの`let`チェックがありますが、`if`に入れず、ネストでしか設定ができません。

```swift:Swift
if let a = a, let b = b {
    a.doSomething()
    b.doSomething()
}
```

同じのコードをKotlinで下記になります。

```kotlin:Kotlin
a?.let {
    b?.let {
        a.doSomething()
        b.doSomething()
    }
}
```

ただし、`if`条件で`null`チェックをしますと、Nullableの関数を呼ぶ時またチェックは要りませんので、下記の実装が十分だと思います。

```kotlin:Kotlin
if (a != null && b != null) {
    a.doSomething()
    b.doSomething()
}
```

## Android

KotlinでAndroidの開発に関しては、下記の２点を気になりました：

* Javaよりビルド時間が遅い（ [参考](https://medium.com/keepsafe-engineering/kotlin-vs-java-compilation-speed-e6c174b39b5d)）
* Javaよりパッケージファイルのサイズが大きい

Androidデベロッパーのオフィシャルサイトを参考しますと（[参考](https://developer.android.com/kotlin/faq.html)）：

> The Kotlin runtime adds about 7,000 methods and ~1MB to your debug APK.

アプリにもよると思いますが、以上の点を気にする方は検討した方がいいかもしれませんね。

# まとめ

AndroidでKotlinに変換すると習慣も変わらないといけませんが、IDEのフルサポートとJavaとの互換性で凄く楽です。
スムーズに変換ができますから、スムーズにKotlinを学べると思います。
あと、Swiftの経験を持ってる方におすすめしますね。SwiftからJavaよりKotlinがありだと思います。

個人ではまだ試していませんが、Android Studio 3.0からJava 8のサポートが始まります。Kotlinが苦手の方はそちらを考えてもいいかもしれません。ただし、まだJava 8のフルサポートがされていないため、まだ早いかと思います。

# 参考
Kotlin のオフィシャルサイト
https://kotlinlang.org/

サンプルが沢山入っているKotlinのオンラインコンソール
https://try.kotlinlang.org 

KotlinプロジェクトのGithub
 https://github.com/JetBrains/kotlin 

Kotlin Android Extensions
https://kotlinlang.org/docs/tutorials/android-plugin.html

AnkoプロジェクトのGithub
https://github.com/Kotlin/anko

Kotlin vs Java: Compilation speed（ブログ）
https://medium.com/keepsafe-engineering/kotlin-vs-java-compilation-speed-e6c174b39b5d

AndroidでJava 8サポート
https://developer.android.com/studio/write/java8-support.html

参考になれるおすすめするブログ
 https://antonioleiva.com