Help us understand the problem. What is going on with this article?

Javaで書いてたAndroidエンジニアが初めてKotlinで書いてみた

More than 1 year has passed since last update.

この記事はKotlin Advent Calendar 2016の最終日の投稿です。
前日はrabitarochanさんのKotlin Compiler Plugin (kotlin-allopen) を追いかけるでした。
初日はngsw_taroさんのKotlin2016まとめでした。

はじめに

私自身は2015年の3月ごろからKotlinでのAndorid開発を始めましたので、タイトルに反してますが、Kotlin歴は約2年ぐらいです。また、去年は以下のような記事を書きました。

2016年はKotlinの記念すべきver1.0のリリースの年でしたが、チームのマイクロサービスの一つでもKotlinが使われたり、新規のサービスのいくつかはKotlinで開発され、Android研修合宿でも参加者の半数がKotlinを選択するなど、社内的にもKotlinが盛り上がった一年でした。

この記事ではその社内で行った研修合宿において、今までJavaで書いていたAndroidエンジニアに独学でKotlinで書いてもらって、その後コードレビューで「こうすればもっとKotlinっぽくなるかも?」という観点で指摘した事項をまとめました。
Kotlinは様々な書き方が出来るので、人やチームによってルールは違うとおもいますが、この記事においては、「より記述量を減らす」観点になっているかと思います。
これからKotlinでAndroidを書こうと思っている方の参考に少しでもなれば幸いです。

Javaで書いてたAndroidエンジニアが初めてKotlinで書いてみた

コードレビューの前後のコードの比較を載せます。

getter/setter

Javaだとfieldにgetterやsetterを作ったりしますが、Kotlinのクラスには基本的にはfieldはなく、propertyを用います。

Before

class MyClass {
   private var size : Int = 0

   fun getSize() {
       return size
   }
}

After

class MyClass {
   var size : Int = 0
}

Kotlinのpropertyはgetter/setterを自由に変更できるので、以下のような使い方もできます。

class MyClass {
    val list = mutableListOf<String>()
    var size = 0
        get() = list.size
        private set
}

Constructor

Kotlinはprimary constructorの定義、プロパティの定義をクラス宣言の箇所にまとめて記述することができます

Before

class Person {
    val firstName: String
    val lastName: String
    private var age: Int

    constructor(firstName: String, lastName: String, age: Int) {
        this.firstName = firstName
        this.lastName = lastName
        this.age = age
    }
}

After

class Person(val firstName: String, val lastName: String, private var age: Int) {

}

const

KotlinにはCompile-Time constantsのためのconstという修飾句があります。

Before

class MyClass {
    val TOKEN_KEY = "ACCESS_TOKEN_KEY"
}

After

class MyClass {
    companion object {
        const val TOKEN_KEY = "ACCESS_TOKEN_KEY"
    }
}

listOf, mutableListOf

Kotlinの標準関数にはlistOfmutableListOfという関数が用意されています。

Before

fun getList(): List<String> {
    val list: MutableList<String> = ArrayList()
    list.add("value1")
    list.add("value2")
    return list
}

After

fun getList(): List<String> {
    return listOf("value1", "value2")
}
fun getList() = listOf("value1", "value2")

Pair

KotlinにはPairというクラスがあり、任意のクラスの二つの値のセットを持つことができます。
さらにtoという関数があり、これを用いるとPairの生成が簡単に行えます。

Before

val params : List<Pair<String, Int>> = listOf(Pair("a", 1), Pair("a", 2))

After

val params = listOf("a" to 1, "a" to 2)

toinfixがついている拡張関数ですので、上のような表記が出来ます。
Pairは以下のような使い方も出来ます。

val (k, v) = "a" to 1
v == 1 // true

val map = mapOf("a" to 1, "b" to 2)
map["a"] == 1 // true

Data Class

EntityなどはData Classとして定義するのが良いでしょう。

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

lazy

Delegates propertyのlazyを用いることで、propertyをNon-nullかつvalにすることができます。

Before

var webView: WebView? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_detail)

    webView = findViewById(R.id.webView) as WebView
    webView?.setWebViewClient(WebViewClient())
    webView?.settings?.javaScriptEnabled = true
    webView?.loadUrl("https://kotlinlang.org/")
}

After

val webView by lazy {
    (findViewById(R.id.webView) as WebView).apply {
        setWebViewClient(WebViewClient())
        settings?.javaScriptEnabled = true
    }
}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_detail)
    webView.loadUrl("https://kotlinlang.org/")
}

上記のような用途であればkotterknifeを用いるのが良いかもしれません。用いる際はRetained Fragmentに関するissueに気をつける必要があります。

Smart Cast / Safe Cast

Castを用いる必要がある場面では、Smart Castas?によるSafe Castを用いることで簡潔に書けます。

Before

if (recyclerView?.itemAnimator is SimpleItemAnimator) {
    (recyclerView?.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
}

After (Smart Cast)

recyclerView?.itemAnimator?.let {
    if(it is SimpleItemAnimator) it.supportsChangeAnimations = false
}

After (Safe Cast)

(recyclerView.itemAnimator as? SimpleItemAnimator)?.run {
    supportsChangeAnimations = false
}

kotlin.collections

kotlin.collectionsのパッケージには多くの便利な拡張関数が用意されています。

Before

fun getUrlWithQueryParams(baseUrl: String, param: List<Pair<String, String>>): String {
    var url: String = baseUrl
    var i: Int = 0
    for (pair in param) {
        if (i == 0) {
            url += "?"
        } else {
            url += "&"
        }
        url += (pair.first + "=" + pair.second)
        i++
    }
    return url
}

After

fun getUrlWithQueryParams(baseUrl: String, params: List<Pair<String, String>>) =
params.foldIndexed(baseUrl, {i, url, param -> "$url${if (i == 0) '?' else '&' }${param.first}=${param.second}"}) 

最後に

Kotlinで開発を始めてから、「Kotlin」でのTwitter検索をずっと見続けてますが、この1年はKotlinが広く普及した1年でもう至るところで普通に使われだしたな、という印象です。
2017年はもっと盛り上がるでしょう!

今年も一年お疲れ様でした。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away