[Kotlin ガイド #3] ifとwhenの使い分けと便利な使い方

Kotlinガイド関係の記事のサムネイル

この記事の内容

  • ifとwhenの使い分け
  • ifとwhenの便利な使い方

ifとwhenの基礎

まずは、軽く基本的な使用方法を解説します。知ってる方は読み飛ばしてください。

ifの基礎

if ~ else if ~ else文を使うことで条件に基づいた分岐処理を行うことができます。

一般的なif文を使った例

Kotlin
fun main() {
    println("1 + 2 は?")
    val input1 = readln()

    if (input1.isEmpty()) {
        println("入力が無効です")
        return
    }

    println("2 + 2 は?")
    val input2 = readln()

    if (input2.isEmpty()) {
        println("入力が無効です")
        return
    }

    if (input1 == "3" && input2 == "4") {
        println("正解です")
    } else if (input1 == "3" || input2 == "4") {
        println("どちらか1つが正解です")
    }else {
        println("どちらも間違いです。")
    }
}

if( 条件 )と書くことで、その条件と一致したときだけ{}内に記述した処理が実行されます。

elseは、elseより前に記述したifブロックのすべての条件と一致しなかった場合に{}内に記述した処理が実行されます。

また、if ~ else if ~ elseは「式」と「文」どちらにもなれます。
最初の例では文でしたが、以下はif式を使った例です

if式を使った例

Kotlin
fun main() {
    val value = readln()

    val intValue = value.toIntOrNull()

    // if式を使って文字列を返す
    val resultStr = if(intValue == null || intValue < 0){
        "これは数字ではない、または0なので無効です"
    }else{
        "OK!"
    }

    println(resultStr)
}

場合分けの結果を変数宣言の初期化と同時に値を設定できるのでif式を使うと記述を簡潔にすることができるようになります。

whenの基礎

whenは、Javaのswitch文の進化版の認識でいいと思います。

大きく分けて2つの書き方ができます

  1. 引数あり
  2. 引数なし

引数ありの場合

構文としては少し特殊ですが、理解はし易い書き方だと思います。

if ~ else if ~ elseを使うよりもだいぶ見やすく感じます

Kotlin
fun main() {
    val value = readln()

    when (value) {
        "" -> println("文字が入力されていません")
        "0" -> println("0を入力しましたね?、0は始まりの数字です")
        "1" -> println("1を入力しましたね?、1はとてもいい数字です")
        "2","4","6","8","10" -> println("2の倍数を入力しましたね?")
        else -> println(value)
    }
}

引数なしの場合

引数なしの場合は、もはやif ~ else if ~ elseを完全に置き換えているのでは無いかと思えるほどスマートで使いやすいです。
引数がないので、処理としては一番上の条件式からチェックしていき、条件に該当する処理を実行し、whenブロックを抜けるという処理になります

Kotlin
fun main() {
    println("繰り返したい文字を入力してください")
    val repeatStr = readln()

    println("何回繰り返すか回数を入力してください")
    val repeatCount = readln().toIntOrNull()

    when {
        repeatStr.isEmpty() -> println("エラー: 繰り返す文字が入力されていません")
        repeatCount == null -> println("エラー: 繰り返す回数を整数で入力してください")
        repeatCount == 0 -> println("エラー: 繰り返さないのであれば使わないでください")
        else -> {
            for (i in 1..repeatCount) {
                println(repeatStr)
            }
        }
    }
}

比較材料としてif文を使って同じように書いてみます。
やはり、else ifが冗長で文字列が読みづらい気がしてしまいます。

Kotlin
fun main() {
    println("繰り返したい文字を入力してください")
    val repeatStr = readln()

    println("何回繰り返すか回数を入力してください")
    val repeatCount = readln().toIntOrNull()

    if (repeatStr.isEmpty()) {
        println("エラー: 繰り返す文字が入力されていません")
    } else if (repeatCount == null) {
        println("エラー: 繰り返す回数を整数で入力してください")
    } else if (repeatCount == 0) {
        println("エラー: 繰り返さないのであれば使わないでください")
    } else {
        for (i in 1..repeatCount) {
            println(repeatStr)
        }
    }
}

javaのswitchで実装されていた、「フォールスルー」という機能をwhenでは撤廃されています。
breakをわざわざ書く必要がなくなりバグの温床になる可能性を軽減する事ができるようになりました。

whenはifと同じようにとして作成することができます

式としての使い方

Kotlin
fun main() {
    println("繰り返したい文字を入力してください")
    val repeatStr = readln()

    println("何回繰り返すか回数を入力してください")
    val repeatCount = readln().toIntOrNull()

    val result = when {
        repeatStr.isEmpty() -> "エラー: 繰り返す文字が入力されていません"
        repeatCount == null -> "エラー: 繰り返す回数を整数で入力してください"
        repeatCount == 0 -> "エラー: 繰り返さないのであれば使わないでください"
        else -> {
            var retStr = ""
            for (i in 1..repeatCount) {
                retStr += repeatStr + "\n"
            }
            retStr
        }
    }

    println(result)
}

使い分け

以下が比較的わかりやすいwhenとifの使い分けだと考えています。

Kotlin
package org.example

fun main() {
    println("繰り返したい文字を入力してください")
    val repeatStr = readln()

    println("何回繰り返すか回数を入力してください")
    val repeatCount = readln().toIntOrNull()

    //  バリデーション
    val result = when {
        repeatStr.isEmpty() -> {
            println("エラー: 繰り返す文字が入力されていません")
            false
        }

        repeatCount == null -> {
            println("エラー: 繰り返す回数を整数で入力してください")
            false
        }

        repeatCount == 0 -> {
            println("エラー: 繰り返さないのであれば使わないでください")
            false
        }

        else -> true
    }

    //  出力
    if (result) {
        for (i in 1..repeatCount!!) {
            println(repeatStr)
        }
    }else{
        println("入力が正しくないため、文字列を出力できませんでした")
    }
}

ifは、基本的に「3つの条件分岐」までを想定するのがいいと考えます。
else ifを使う必要が出てきた場合は、whenを使ったほうがいいのでは無いかという考慮をするのが記述を簡潔にたもつ方法だと考えます
つまり、ifを使うときは「条件が二項対立しているときに使う」という認識で使用するのがいいと思います。

whenの場合は、「値をチェックしたい」場合に適しています。
値のチェックというのは、どのような値が来るかわからない = ケースが増える可能性がある。ということなので、whenが適しています。
また、型の判別(型チェック、スマートキャスト)などを行う場合は、whenが適しています。

型チェックやスマートキャストの例

Kotlin
//  ベースクラス
open class Input

//  派生クラス
class UserNameInput(val userName: String) : Input()
class EmailInput(val email: String) : Input()
class PasswordInput(val pass: String) : Input()

//  バリデーション関数
fun validateInput(input: Input): Pair<Boolean, String> {
    return when (input) {
        is UserNameInput -> {
            return when {
                input.userName.isEmpty() -> Pair(false, "ユーザー名が入力されていません")
                input.userName.length > 5 -> Pair(false, "ユーザー名が長すぎます")
                input.userName.length < 2 -> Pair(false, "ユーザー名が短すぎます")
                else -> Pair(true, "OK")
            }
        }

        is EmailInput -> {
            return when {
                input.email.isEmpty() -> Pair(false, "メールアドレスが入力されていません")
                !input.email.contains("@") -> Pair(false, "メールアドレスの形式が不正です")
                input.email.length< 10 -> Pair(false, "メールアドレスが短すぎます")
                else -> Pair(true, "OK")
            }
        }

        is PasswordInput -> {
            return when {
                input.pass.isEmpty() -> Pair(false, "パスワードを入力してください")
                input.pass.length < 5 -> Pair(false, "パスワードが短すぎます")
                else -> Pair(true, "OK")
            }
        }

        else -> Pair(false, "不明な入力です")
    }
}

fun main() {
    println("ユーザー名を入力してください")
    val userName = readln()
    println("メールアドレスを入力してください")
    val email = readln()
    println("パスワードを入力してください")
    val password = readln()

    //  配列にぶち込む
    val inputs = listOf(
        UserNameInput(userName = userName),
        EmailInput(email = email),
        PasswordInput(pass = password),
    )

    for (input in inputs) {
        val result = validateInput(input)

        if (!result.first) {
            println(result.second)
        }
    }
}

まとめ

ifとwhenの使い分けをまとめると、以下のようになります

  • if
    • 3つの条件分岐で足りる場合
    • 2項対立している場合
  • when
    • 値による場合分けが必要な場合
    • 3つ以上の条件分岐が必要か、必要になる可能性がある場合
    • 型チェック・スマートキャストを使いたい場合

参考リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です