おはようエンジニア

まともなエンジニアになりたい

『Kotlinイン・アクション』第2章を読む (その2)

前回に引き続き、Kotlinイン・アクションの第2章「Kotlinの基本」です。

『Kotlinイン・アクション』第2章を読む (その1) - おはようエンジニア

 enum

  • 列挙型は enum class で宣言する
    • enum の部分はソフトキーワードという扱い(classと組み合わせて意味を持つ)
enum class Color {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
  • プロパティを持つ列挙型も宣言できる
    • enum定数の宣言ではプロパティの値を指定する
    • 定数定義の終わりにセミコロンが必要 (メソッドの定義と分離するため)
enum class Color (val r: Int, val g: Int, val b: Int){
    RED(255, 0, 0), ORANGE(255, 165, 0), ..., VIOLET(238, 130, 238);

    fun rgb() = (r * 256 + g) * 256 + b
}

when

  • whenは値を返すので「文」ではなく「式」
    • それぞれの分岐でのbreak文は不要
    • 複数の値によって分岐できる
fun getWarmth(color: Color) = when(color) {
    Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
    Color.GREEN -> "neutral"
    Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
}
  • 定数の値をインポートするとより簡潔
    • ここの1行目の役割がわからない
import ch02.colors.Color
import ch02.colors.Color.*

fun getWarmth(color: Color) = when(color) {
    RED, ORANGE, YELLOW -> "warm"
    GREEN -> "neutral"
    BLUE, INDIGO, VIOLET -> "cold"
}
  • when式の引数にオブジェクトも使用できる
    • この例は比較の度にインスタンスを生成するのでパフォーマンスはあまりよくない
    • 読みにくくなるが引数なしwhen文を使うとパフォーマンスは改善する
// オブジェクトが引数の場合
when (setOf(c1, c2)) {
  setOf(RED, YELLOW) -> ORANGE
  ...
}

// 引数なしの場合
when {
  (c1 == RED && c2) || (c1 == YELLOW && c2 == RED) -> ORANGE
  ...
}

スマートキャスト

is によって変数の型をチェックすると明示的なキャストが不要になる

fun eval(e: Expr): Int {
    if (e is Num) {
        val n = e as Num   // 不要
        println("num: ${n.value}")
        return n.value
    } else if (e is Sum) {
        val n = e as Sum   // 不要
        ...
}

whenですっきり表記

fun eval(e: Expr): Int {
    when (e) {
        is Num -> {
            println("num: ${e.value}")
            e.value
        }
        is Sum -> {
        ...
}

while

  • whiledo-while がある

for と range

  • Kotlin の forJavafor-each 形式のみ
// 1から100まで (閉区間)
for (i in 1..100) {
    print(i) // 1,2,3,...,100
}

// 1から100の直前まで (半開区間)
for (i in 1 until 100) {
    print(i) // 1,2,3,...,99
}

// 後進
for (i in 100 downTo 1) {
    print(i) // 100,99,98,...,1
}

// 2ずつ増加
for (i in 1..100 step 2) {
    print(i) // 1,3,5,...,99
}
  • 文字やコレクションでもレンジを作れる
// Character
for (c in 'A' .. 'F') {
    ...
}

// TreeMap
for ((key, value) in myMap) {
    ...
}

// ArrayList
for ((index, element) in list.withIndex()) {
    ...
}
  • !in でレンジ外であることをチェックできる
fun isNotDigit(c: Char) = c !in '0'..'9'

例外処理

  • throw は式扱い
  • 関数定義の後ろに throws はいらない
  • 検査例外と非検査例外は区別しない
    • 例外を処理するかどうかは実装者に委ねる
    • 理由はJavaの検査例外の処理がボイラープレートになっているため
  • tryも式
    • 例外をキャッチしたときに結果を変える実装をシンプルに書ける
val number = try {
    Integer.parseInt(reader.readLine())
} catch (e: NumberFormatException) {
    null
}

式と文のまとめ

構文 Java Kotlin
when
if
throw
try
代入 (例: a = 1)

おわりに

2章で基本的な構文について学ぶことができました。

KotlinではJavaで文だったものの一部が式になり、式本体を持つ関数と相性が良さそうな印象です。

特に、when式はかなり柔軟で使い勝手がよさそうです。また、break文を何度も書く必要がないのも嬉しいです。