この記事は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の標準関数にはlistOf、mutableListOfという関数が用意されています。
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)
to
はinfix
がついている拡張関数ですので、上のような表記が出来ます。
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 Castやas?
による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年はもっと盛り上がるでしょう!
今年も一年お疲れ様でした。