Skip to content

条件とループ

If式

Kotlinでは、ifは式(expression)であり、値を返します。 そのため、通常のifがその役割を十分に果たすため、三項演算子(condition ? then : else)は存在しません。

kotlin
fun main() {
    val a = 2
    val b = 3

    var max = a
    if (a < b) max = b

    // With else
    if (a > b) {
      max = a
    } else {
      max = b
    }

    // As expression
    max = if (a > b) a else b

    // You can also use `else if` in expressions:
    val maxLimit = 1
    val maxOrLimit = if (maxLimit > a) maxLimit else if (a > b) a else b
  
    println("max is $max")
    // max is 3
    println("maxOrLimit is $maxOrLimit")
    // maxOrLimit is 3
}

if式のブランチはブロックにすることができます。この場合、最後の式がブロックの値となります。

kotlin
val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

ifを式として使用する場合、例えばその値を返したり、変数に代入したりする際には、elseブランチは必須です。

When式とwhen文

whenは、複数の可能な値や条件に基づいてコードを実行する条件式です。これはJava、Cなどの言語におけるswitch文に似ています。例を挙げます。

kotlin
fun main() {
    val x = 2
    when (x) {
        1 -> print("x == 1")
        2 -> print("x == 2")
        else -> print("x is neither 1 nor 2")
    }
    // x == 2
}

whenは、何らかのブランチ条件が満たされるまで、その引数をすべてのブランチと順に照合します。

whenにはいくつかの異なる使い方があります。まず、whenは**式(expression)としても文(statement)**としても使用できます。 式として使用する場合、whenは後でコードで使用するために値を返します。文として使用する場合、whenはそれ以上何も返さずにアクションを完了します。

式(Expression)文(Statement)
kotlin
// Returns a string assigned to the 
// text variable
val text = when (x) {
    1 -> "x == 1"
    2 -> "x == 2"
    else -> "x is neither 1 nor 2"
}
kotlin
// Returns nothing but triggers a 
// print statement
when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> print("x is neither 1 nor 2")
}

次に、whenを対象(subject)ありで、または対象なしで使用できます。対象をwhenで使用するかどうかにかかわらず、式または文の動作は同じです。可能な場合は対象ありのwhenを使用することをお勧めします。そうすることで、何をチェックしているかが明確になり、コードが読みやすく、保守しやすくなります。

対象xあり対象なし
kotlin
when(x) { ... }
kotlin
when { ... }

whenをどのように使用するかによって、ブランチで考えられるすべてのケースをカバーする必要があるかどうかの要件が異なります。

whenを文として使用する場合、考えられるすべてのケースをカバーする必要はありません。この例では、一部のケースがカバーされていないため、何も起こりません。しかし、エラーは発生しません。

kotlin
fun main() {
    val x = 3
    when (x) {
        // Not all cases are covered
        1 -> print("x == 1")
        2 -> print("x == 2")
    }
}

when文では、個々のブランチの値は無視されます。ifと同様に、各ブランチはブロックにすることができ、その値はブロック内の最後の式の値となります。

whenを式として使用する場合、考えられるすべてのケースをカバーする必要があります。つまり、**網羅的(exhaustive)**でなければなりません。最初に一致したブランチの値が、全体の式の値になります。すべてのケースをカバーしない場合、コンパイラはエラーをスローします。

when式に対象がある場合、考えられるすべてのケースがカバーされていることを確認するためにelseブランチを使用できますが、必須ではありません。例えば、対象がBooleanenumクラスsealedクラス、またはそれらのnull許容型の場合、elseブランチなしで全ケースをカバーできます。

kotlin
enum class Bit {
    ZERO, ONE
}

val numericValue = when (getRandomBit()) {
    // No else branch is needed because all cases are covered
    Bit.ZERO -> 0
    Bit.ONE -> 1
}

when式を簡素化し、繰り返しを減らすには、コンテキスト依存の解決(現在プレビュー中)を試してください。 この機能を使用すると、予想される型が分かっている場合にwhen式でenumエントリやsealedクラスのメンバーを使用する際に型名を省略できます。

詳細については、コンテキスト依存の解決のプレビューまたは関連するKEEP提案を参照してください。

when式に対象がない場合elseブランチが必須であり、そうでないとコンパイラはエラーをスローします。 elseブランチは、他のどのブランチ条件も満たされない場合に評価されます。

kotlin
val message = when {
    a > b -> "a is greater than b"
    a < b -> "a is less than b"
    else -> "a is equal to b"
}

when式と文は、コードを簡素化し、複数の条件を処理し、型チェックを実行するためのさまざまな方法を提供します。

複数のケースの共通の動作は、コンマで区切って1行に結合することで定義できます。

kotlin
when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

ブランチ条件として、定数だけでなく任意の式を使用できます。

kotlin
when (x) {
    s.toInt() -> print("s encodes x")
    else -> print("s does not encode x")
}

inまたは!inキーワードを使用して、値が範囲またはコレクションに含まれているかどうか、あるいは含まれていないかをチェックすることもできます。

kotlin
when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

さらに、isまたは!isキーワードを使用して、値が特定の型であるかどうか、あるいはそうでないかをチェックできます。なお、スマートキャストにより、追加のチェックなしで型のメンバー関数やプロパティにアクセスできます。

kotlin
fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}

whenは、if-else ifチェーンの代替として使用できます。 対象がない場合、ブランチ条件は単純なブール式になります。trueとなる最初のブランチが実行されます。

kotlin
when {
    x.isOdd() -> print("x is odd")
    y.isEven() -> print("y is even")
    else -> print("x+y is odd")
}

次の構文を使用して、対象を変数にキャプチャできます。

kotlin
fun Request.getBody() =
    when (val response = executeRequest()) {
        is Success -> response.body
        is HttpError -> throw HttpException(response.status)
    }

対象として導入された変数のスコープは、when式または文の本体に限定されます。

when式におけるガード条件

ガード条件を使用すると、when式のブランチに複数の条件を含めることができ、複雑な制御フローをより明示的かつ簡潔にします。 ガード条件は、対象を持つwhen式または文で使用できます。

ブランチにガード条件を含めるには、プライマリ条件の後にifで区切って配置します。

kotlin
sealed interface Animal {
    data class Cat(val mouseHunter: Boolean) : Animal
    data class Dog(val breed: String) : Animal
}

fun feedAnimal(animal: Animal) {
    when (animal) {
        // プライマリ条件のみのブランチ。`animal`が`Dog`の場合に`feedDog()`を呼び出す
        is Animal.Dog -> feedDog()
        // プライマリ条件とガード条件の両方を持つブランチ。`animal`が`Cat`であり、かつ`mouseHunter`ではない場合に`feedCat()`を呼び出す
        is Animal.Cat if !animal.mouseHunter -> feedCat()
        // 上記のどの条件も一致しない場合に"Unknown animal"と表示する
        else -> println("Unknown animal")
    }
}

単一のwhen式内で、ガード条件を持つブランチと持たないブランチを組み合わせることができます。 ガード条件を持つブランチ内のコードは、プライマリ条件とガード条件の両方がtrueと評価された場合にのみ実行されます。 プライマリ条件が一致しない場合、ガード条件は評価されません。

elseブランチを持たないwhen文でガード条件を使用し、どの条件も一致しない場合、どのブランチも実行されません。

一方、elseブランチを持たないwhen式でガード条件を使用する場合、ランタイムエラーを避けるために、考えられるすべてのケースを宣言することがコンパイラによって要求されます。

さらに、ガード条件はelse ifをサポートしています。

kotlin
when (animal) {
    // `animal`が`Dog`であるかをチェックする
    is Animal.Dog -> feedDog()
    // `animal`が`Cat`であり、かつ`mouseHunter`ではないかをチェックするガード条件
    is Animal.Cat if !animal.mouseHunter -> feedCat()
    // 上記のどの条件も一致せず、animal.eatsPlantsがtrueの場合にgiveLettuce()を呼び出す
    else if animal.eatsPlants -> giveLettuce()
    // 上記のどの条件も一致しない場合に"Unknown animal"と表示する
    else -> println("Unknown animal")
}

複数のガード条件を単一のブランチ内で、ブール演算子&& (AND) または|| (OR) を使用して組み合わせることができます。 混乱を避けるために、ブール式を括弧で囲んでください。

kotlin
when (animal) {
    is Animal.Cat if (!animal.mouseHunter && animal.hungry) -> feedCat()
}

ガード条件は、対象を持つすべてのwhen式または文で使用できます。ただし、コンマで区切られた複数の条件がある場合(例: 0, 1 -> print("x == 0 or x == 1"))は除きます。

Forループ

forループは、イテレーターを提供するあらゆるものを反復処理します。これは、C#のような言語におけるforeachループに相当します。 forの構文は次のとおりです。

kotlin
for (item in collection) print(item)

forの本体はブロックにすることができます。

kotlin
for (item: Int in ints) {
    // ...
}

前述のとおり、forはイテレーターを提供するあらゆるものを反復処理します。これは、以下の条件を満たすことを意味します。

  • Iterator<>を返すメンバー関数または拡張関数iterator()を持つこと。そして、そのIterator<>は:
    • メンバー関数または拡張関数next()を持つこと。
    • Booleanを返すメンバー関数または拡張関数hasNext()を持つこと。

これら3つの関数はすべてoperatorとしてマークされている必要があります。

数値の範囲を反復処理するには、範囲式を使用します。

kotlin
fun main() {
    for (i in 1..3) {
        print(i)
    }
    for (i in 6 downTo 0 step 2) {
        print(i)
    }
    // 1236420
}

範囲または配列に対するforループは、イテレーターオブジェクトを作成しないインデックスベースのループにコンパイルされます。

インデックスを使用して配列やリストを反復処理したい場合は、次のように行えます。

kotlin
fun main() {
val array = arrayOf("a", "b", "c")
    for (i in array.indices) {
        print(array[i])
    }
    // abc
}

あるいは、withIndexライブラリ関数を使用することもできます。

kotlin
fun main() {
    val array = arrayOf("a", "b", "c")
    for ((index, value) in array.withIndex()) {
        println("the element at $index is $value")
    }
    // the element at 0 is a
    // the element at 1 is b
    // the element at 2 is c
}

Whileループ

whiledo-whileループは、条件が満たされている間、継続的にその本体を処理します。 両者の違いは、条件をチェックするタイミングです。

  • whileは条件をチェックし、満たされていれば本体を処理し、再び条件チェックに戻ります。
  • do-whileは本体を処理してから条件をチェックします。条件が満たされていれば、ループを繰り返します。そのため、do-whileの本体は条件にかかわらず少なくとも一度は実行されます。
kotlin
while (x > 0) {
    x--
}

do {
    val y = retrieveData()
} while (y != null) // y is visible here!

ループにおけるbreakとcontinue

Kotlinは、ループにおける従来のbreakおよびcontinue演算子をサポートしています。戻り値とジャンプを参照してください。