はじめに
これは2017/04/05に公開されたKotlin 1.1 is also for Android Developersの日本語訳エントリとなります。
ショボい英語力と足りないKotlin力を使って書きましたので、変なところが散見するかと思いますが、
大目に見る/優しくご指摘いただければ幸いですmm
ーーー 修正履歴 ーーー
- 2017/05/11 02:02: 原文へのリンクを追加。ソースを一箇所修正。(@jollyjoester様より)
ーーーーーーーーーーー
日本語訳ここから
Kotlin 1.1リリースktkr。このリリースに含まれる新しい機能はJavaで開発するエンジニアにとって非常に有用なだけでなく、
JVMを新しい可能性の世界へといざないます。
しかしcoroutinesやtype aliasesのような機能のなかには、Android開発者にとってSF(Science Fiction)にも見えるものがあります。
Kotlinはどのようなプラットフォームでも開発者がほとんど気にしなくても良い方法で少しづず改善するため、未だにJava6で立ち往生しています。
なのでこのような質問があるでしょう: 「今回の新機能を有効にしたままJava6との互換性は保てる?」
そして答えはこうです: 「もちろんさ!」
すべての新機能はJava6でも、そしてAndroid開発者も利用可能です。
そして今日その中の幾つかを、Androidアプリを作るときにどのように使えるのかをお見せしたいと思います。
Type aliases: 自作のリスナをより読みやすくすることが出来ます
もちろんtype aliasesは様々な利用法がありますが、一番最初に脳裏によぎったのはlambdaを利用しながらもリスナの可読性を向上させることが出来る能力です。
もしtype aliasesというワードが初耳であれば、これは基本的に複雑な型により読みやすい名前を与えるものと捉えていただければと思います。
例えば、listenerを受け取るRecyclerView.Adapterの拡張クラスを作ることを考えます。
ご存知の通り、RecyclerViewはリストのアイテムクリックを扱う標準(ListViewが持っているような)の方法を持っていません。
そのため、自分で用意する必要があります。
viewにアクセス出来るlistenerが必要なことを考えると、Adapterクラスは以下のようにできます。
class MyAdapter(val items: List<Item>, val listener: (View) -> Unit) : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
...
}
そしてViewHolderはそのlistenerを、viewのclick listenerにアサインするために必要とするでしょう。
(以下のようにできます)
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: Item, listener: (View) -> Unit) {
itemView.setOnClickListener(listener)
}
}
これはそれほど複雑な例ではありませんが、例に見られるようにlambdaを繰り返す必要があり、これは読みづらさを招く可能性があります。
((View) -> Unit)こういう部分ですね)
このことに対して、click listenerを意味するtype aliasを定義することができ、
typealias ClickListener = (View) -> Unit
これを用いてlistenerを使う部分を以下のように置換することができます。
class MyAdapter(val items: List<Item>, val listener: ClickListener)
fun bind(item: Item, listener: ClickListener) { ... }
Data classes: より強力になりました
Data classは大量の雛形が出来上がってしまうのを避けることができるすごいものですが、
いくつかの力不足のために不便になってしまうケースがあります。
Kotlin 1.1の新機能の1つが継承に関するものです。
Data classが他のクラスを継承できるようになりました。
これはdata classがsealed classの一部になることを許容します。
sealed class UiOp {
object Show : UiOp()
object Hide : UiOp()
data class Translate(val axis: Axis, val amount: Int): UiOp()
}
(Kotlin 1.0等でdata classを他のクラスを継承して定義しようとすると、
Data class inheritance from other classes is forbiddenと怒られます。)
さらに、sealed classのサブクラスが親クラスの外で定義できるようになったため、次のようにも書けるようになりました。
(参考: https://kotlinlang.org/docs/reference/sealed-classes.html)
sealed class UiOp
object Show : UiOp()
object Hide : UiOp()
data class Translate(val axis: Axis, val amount: Int)
lambda内でのdata classのデストラクション
Data classは自動生成されるcomponentX()メソッドのお陰でkotlin創生の時代からデストラクションすることができ、
次のようにしてdata classのデータを変数に分解することができました。
data class Item(val text: String, val url: String)
val (text, url) = item
しかし、lambda内で同じことをするという本当に強力な機能がかけていました。
もう待たなくていいんだよ!今はこんなふうにできます。
fun bind(item: Item) = item.let { (text, url) ->
textView.text = text
imageView.loadUrl(url)
}
これは例えばPairsや、Mapでのkey/valueセットを使う際にとても役立ちます。
ローカルで移譲されたプロパティ
移譲されたプロパティはクラスのプロパティに追加の機能をもたせるのに非常に便利であることが証明されています。
例えば、最も便利なものの1つに、プロパティ割当の実行をそのプロパティが最初に使われる時点まで延期するlazy delegationが挙げられます。
これはローカル変数に関しても役立つはずですが、Kotlinではその機能はありませんでした。
Kotlin 1.1では、ローカル変数に関しても次のようにlazyなどの移譲を使うことができます。
fun testLocalDelegation() {
val database by lazy { createDatabase() }
val cache by lazy { createMemoryCache() }
if (mustUseDatabase()) {
database.use { ... }
} else {
cache.use { ... }
}
}
この例はlazyがなくても実現できますが、コンセプトの理解には役立ちます。
もしあるメソッドが使われるかもしれないし使われないかもしれない1組の巨大なオブジェクトを保持するとします。
lazyを用いることによって、それを本当に使われる時が来るまで延期することができます。
(使われなかった時、無駄に巨大なオブジェクトを持ってしまうことを避けられます)
もうlambda内で使わない変数を宣言しなくて良くなります
lambda内で、最後まで使われない変数であっても引数として宣言しなければならならないのはもはや一般的なことでした。
Kotlin 1.0では使わない変数を破棄する方法がなかったからです。
例として、この記事でRecyclerViewを移譲を利用して更新する方法を紹介した際、
以下のようにしました。
var items: List<Content> by Delegates.observable(emptyList()) {
prop, old, new ->
autoNotify(old, new) { o, n -> o.id == n.id }
}
今までは変数propは1度も使われていませんが宣言する必要がありました。
Kotlin 1.1では_(アンダースコア)を使って宣言を避けることができます。
var items: List<Content> by Delegates.observable(emptyList()) {
_, old, new ->
autoNotify(old, new) { o, n -> o.id == n.id }
}
しかしこれは、どの引数も使わない場合はまだイマイチよくありません。
もしlambdaの引数が1つ以上ある場合、どれも使わないにもかかわらず全ての引数について記述する必要があります。
var items: List<Item> by Delegates.observable(emptyList()) {
_, _, _ ->
notifyDataSetChanged()
}
これは変数定義をより少なくするだけでなく、コードがより読みやすくなります。
もうこれからは、その変数が使われているのかいないのか調べる必要はなく、使われていないことがはっきりします。
Coroutines
CoroutinesはKotlin 1.1で最も興奮するニュースです。このリリースでは"実験的"とは言われているものの、
それらは完全に機能しており、今からでもあなたのプロジェクトで利用することができます。
Coroutinesは非同期なコードを同期的に書くことができ、逐次的に書いているにも関わらず後続処理を保留して結果を待つことができます。
(async-await)
KotlinのCoroutinesについて既にご存知であろう1つは、これはライブラリや特定の実装ではなく言語仕様でありCoroutinesを利用してライブラリを作ることが出来るということです。
なので、最終的なコードとしてはあまり変わらないとしても、何がサブスレッドを作っている歯車で、メインスレッドに返却している歯車なのかを理解することはAndroidではとても重要になります。
幸運なことに、Kotlinのコミュニティは非常に活発に活動しており、既にいくつかのライブラリがCoroutinesの力をAndroidに提供しています。
いくつか例を上げておきます。
- まず最初に見ておきたいであろうJetBrainsによって提供されている公式ライブラリです
-
kotlinx-coroutines-android: Androidで
Coroutinesを利用するための実装を提供 -
Anko: 最新betaから多くのframeworkのlistenerをサポートするための
Coroutinesを提供
-
kotlinx-coroutines-android: Androidで
- 独自の
Coroutinesを実装し提供しているサードパーティライブラリも存在します- AsyncAwait-Android by Niek Haarman
- Async / Await by Metalab
- kotlin-coroutines-retrofit by Andrey Mischenko: Retrofitをサポートしたい場合はこちら
これらを使うだけでなく、これらがどのように実装されているのかを確認してみることを強くおすすめします。
それがオープンソースの魔法です。
Android開発でのその他のクールなやつら
今回のリリースにはもっと多くの改善が含まれていますが、ここではAndroid開発にフォーカスしてご紹介したいと思います。
まず最初に、jackOptions { true }を用いたJack compilerのサポートです。GoogleはJack toolchainを非推奨として公表していますが、Java8のために利用している場合、Jack toolchainはAndroid 2.4のリリースまでは有用に使うことができるでしょう。
(実際にはAndroid Studio 2.4 Preview 4以降ではJackが廃止され、新しい方法でJava 8の言語機能がサポートされているようです[参考])
さらに、カスタムViewのコンストラクタを実装するための@JvmOverloadsを使う新しいintentionもあります。
これは、カスタムViewのコンストラクタを引数のデフォルト値を利用して一行で書けるようにするためのものです。(少々長い一行ですが^^;)
class CustomView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
...
}
この@JvmOverloadsですが、調べてみると2015年には既に使えていた機能らしいです。
特に目新しいものでもなさそうなので、あくまで便利な機能を紹介するために書いているということですかね?[参考]
まとめ
Kotlin 1.1はなんでまだJavaを使い続けてるの?というもっともな疑問を投げかけるほど素晴らしい機能を多くもたらしました。
KotlinがAndroid開発者にもたらした疑いの余地がなく、今日からでもAndroidアプリをKotlinで書き始めることができます。
そしてAndroidアプリを1からKotlinで作る方法を学びたいのであれば、興味をもてる本を見つけることでしょう。
日本語訳ここまで
おわりに
async-awaitを実現できるCoroutines素敵ですね。
Node.jsなどでは既に使えていただけに、遂に来たかという感じです。
以上となります。
Kotlinかわいい