Kotlin の基礎を学ぶなら Kotlin Playground で 🏕️
Kotlin を web で試せる遊び場
play.kotlinlang.org で Kotlin を実行して遊ぼう!コードエディタ (画面上部) と標準出力 (画面下部) だけのシンプルな構成だから、とっつきやすいのも良いところ。Android アプリ開発で必要な Kotlin 基礎を学ぶのにぴったりだ。
Haskell にも似た遊び場がある (play.haskell.org)。こういうのってクールだよね😎
初心者向けの Android 公式チュートリアルをやってみた
developer.android.com/courses には 3 つの研修教材がある。[初心者向け]、[経験豊富な Android デベロッパー向け]、そして [認定資格プログラム]。このうち [初心者向け] に提供されている [Compose を用いた Android アプリ開発の基礎] は、8 つのユニットで構成される教材だ。
この記事では、ユニット 2 [アプリ UI を作成する] に含まれる 3 つのパスウェイのうち、最初の 1 つ [Kotlin の基礎] の学びと感想をメモする。ユニット 1 をやったのは 4 ヶ月も前 (2024 年 9 月) で、その内容はすでに忘れかけてる…😅

以下では、[アクティビティ 2] から [アクティビティ 5] について感想を書く。[ユニット 2] > [パスウェイ 1] は、[アクティビティ 1] から [アクティビティ 7] までの計 7 つを含むけど、1、6、7 は教材じゃないのでね (1 は導入、6 は問題集、7 はまとめ)。
2 Kotlin で条件を記述する
[Kotlin で条件を記述する] では、if と when を学ぶ。また条件の記述に x in 0..10 のような記法が使えることも学ぶ。後者は僕にとって新しかったけど、その他はかなり基本的な内容だから簡単だった。
3 Kotlin で null 可能性を使用する
[Kotlin で null 可能性を使用する] では、nullable な変数を学ぶ。nullable な変数を宣言するには var x: Int? のように書く。これは「整数型であり、値に null を許容する変数 x を宣言する」という意味 (のはず)。例えばこんなふうに使う。
fun main() {
val x: String? = null
println("Length of 'x' is ${x?.length ?: 0}")
}
// 出力
Length of 'x' is 0
nullable な変数の property や method にアクセスする際は、. ではなくセーフコール演算子 ?. を使う。JavaScript のオプショナルチェーンを見たことがあるから、連想で理解した。セーフコール演算子 ?. の結果が null のとき、エルヴィス演算子 ?: を使ってフォールバック値を設定できる。
4 Kotlin でクラスをオブジェクトを使用する
class の field を constructor で指定するには class MyClass(var myField: String) {} のように書く。OOP の概念はざっくり浅く理解してるつもりだけど、それをコードで表現するのは初めてだから手こずった。[Kotlin でクラスとオブジェクトを使用する] の、僕の理解度は怪しい🫠
コンパイラは、primary と secondary のいずれの constructor が呼び出されたかを、呼び出し側が渡す引数の型と個数で判別するらしい。したがって、primary と同じ型の引数を同じ数だけ受け取る secondary constructor を作ることはできない (はず)。これ教材に書いてなかった気がする。
class SmartDevice(val name: String, val category: String) {
var deviceStatus = "online"
constructor(name: String, category: String, statusCode: Int) : this(name, category) {
deviceStatus = when (statusCode) {
0 -> "offline"
1 -> "online"
else -> "unknown"
}
}
...
}
// primary constructor を呼び出すならこう
val myDevice1 = SmartDevice(name = "Pixel", category = "High-end")
// secondary constructor を呼び出すならこう
val myDevice2 = SmartDevice(name = "iPhone", category = "Middle-calss", statusCode = 0)
親を継承した子 class を定義するには、class SubClass(val param2: Int) : SuperClass(val param1: Int) {} のように書く。
下の表は、可視性修飾子の端的なまとめ。僕はまだ小さいプログラムしか作ってないから、有り難みの実感がない。
| 修飾子 | 同じクラスでアクセス可能 | サブクラスでアクセス可能 | 同じモジュールでアクセス可能 | モジュール外からアクセス可能 |
|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
protected | ✅ | ✅ | ❌ | ❌ |
internal | ✅ | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ | ✅ |
移譲 class (インタフェース?) の実装を実践したけど、実は全然分かってない。class MyInterface() : ReadWriteProperty<Any?, Int> {} を見れば、ReadWriteProperty 型の class を定義したことは分かる。それで、<Any?, Int> は何?教材は「気にしないでください」と言ってるので、そのうち解説されるのかな…?
5 Kotlin で関数型とラムダ式を使用する
[Kotlin で関数型とラムダ式を使用する] は楽しい。関数を変数に代入する方法が 2 つ紹介されてた。
- 関数
fun myFunctionを変数val xに代入するには、val x = ::myFunctionと書く - Lambda 式
{}を使用して、val x = { ... function body ... }と書く。- Lambda 式では、最後の行がその返り値となる (と思う。教材に明示的に書かれてない)
- 最後の行が式なら、それが返り値
- 最後の行が文なら、返り値は
Unit(返り値なし)
Lambda 式では、-> の前に引数の変数名を定義する。1 行目の最後の quantity がそれ。
val coins: (Int) -> String = { quantity ->
"$quantity quarters"
}
(上の例のように) 取る引数が 1 つだけの場合、引数を入れる変数の名前を省略できる。省略した場合は、関数本体で変数 it として呼び出せる。
val coins: (Int) -> String = {
"$it quarters"
}
関数にも型がある。関数の型は (Int) -> String のように書いて、「Int を受け取って String を返す関数型」と読む。変数名は書かず、入出力の型のみを書くのがポイント。そりゃそうだよね、型を記述するために、入出力の変数名まで指定する必要が無い。
fun trickOrTreat(
isTrick: Boolean,
extraTreat: (Int) -> String
): () -> Unit {
//... function body ...
}
例えば上のように定義される関数 trickOrTreat は、「引数に isTrick と extraTreat を取り、() -> Unit 型の関数を返す関数」だ。isTrick は Boolean (分かりやすい) で、extraTreat は (Int) -> String 型の関数だ。
関数型を引数に取る関数に対し、Lambda 式を直接渡すことができる。例えば上の関数 trickOrTreat() に渡す引数 extraTreat を、下のようにインラインで定義できる。Lambda 式 { "$it quarters" } の型は (Int) -> String なので、関数 trickOrTreat() の引数の型に合う。
val treatFunction = trickOrTreat(false, { "$it quarters" })
🤔 Lambda 式 { "$it quarters" } の型は、この定義だけを見ても分からない (はず)。分かるのは (型不明の 1 つの引数) -> String 型であるということ。この Lambda 式を受け取る関数 trickOrTreat() の引数の定義を見て、これが (Int) -> String 型だと判明する (のだと思う)。
そして最重要なのが、後置 Lambda 構文だ。ある関数の最後の引数が関数のとき、引数を渡す括弧の外に Lambda 式を書いて、その関数の「関数を取る引数」に関数を渡せる。例えば上の関数 trickOrTreat() でも後置 Lambda 構文が使える。
val treatFunction = trickOrTreat(false) { "$it quarters" }
何が嬉しいのかまだしっかりと理解してないけど、おそらく利点は「読みやすい」だろう。例えば上の trickOrTreat(false) { "$it quarters" } は、普通の関数定義のようにも見えるよね (僕はまだ見慣れてないので it が唐突に感じるけど)。
