1前書き

このチュートリアルでは、Kotlin言語の

when \ {}

ブロックを紹介し、それを使用できるさまざまな方法を説明します。

この記事の内容を理解するためには、Kotlin言語の基本的な知識が必要です。言語の詳細については、Baeldungの/kotlin[Kotlin言語の紹介]の記事を参照してください。


2 Kotlinの

when \ {}

Block


  • {block}が本質的にJavaで知られている

    switch-case __文の高度な形式である場合

Kotlinでは、一致するcaseが見つかった場合、それぞれのcaseブロック内のコードのみが実行され、実行は

when

ブロックの後の次のステートメントから続行されます。これは本質的に、各

case

ブロックの最後にbreak文が必要ないことを意味します。


when \ {}

の使用方法を説明するために、Unixのいくつかのファイルタイプについて、許可フィールドの最初の文字を保持するenumクラスを定義しましょう。

enum class UnixFileType {
    D, HYPHEN__MINUS, L
}

それぞれのUnixファイルタイプをモデル化するクラスの階層も定義しましょう。

sealed class UnixFile {

    abstract fun getFileType(): UnixFileType

    class RegularFile(val content: String) : UnixFile() {
        override fun getFileType(): UnixFileType {
            return UnixFileType.HYPHEN__MINUS
        }
    }

    class Directory(val children: List<UnixFile>) : UnixFile() {
        override fun getFileType(): UnixFileType {
            return UnixFileType.D
        }
    }

    class SymbolicLink(val originalFile: UnixFile) : UnixFile() {
        override fun getFileType(): UnixFileType {
            return UnixFileType.L
        }
    }
}


2.1. 式としての\ {} __の場合

Javaのswitch文との大きな違いは、

Kotlinの

when \ {}

ブロックは、文としても式としても使用できることです。

Kotlinは、他の関数型言語の原則に従い、フロー制御構造は式であり、それらの評価の結果は呼び出し元に返すことができます。

返された値が変数に代入されている場合、コンパイラは戻り値の型がクライアントが期待している型と互換性があるかどうかをチェックし、そうでない場合は通知します。

@Test
fun testWhenExpression() {
    val directoryType = UnixFileType.D

    val objectType = when (directoryType) {
        UnixFileType.D -> "d"
        UnixFileType.HYPHEN__MINUS -> "-"
        UnixFileType.L -> "l"
    }

    assertEquals("d", objectType)
}

Kotlinの式としてwhenを使用するときに注意することが2つあります。

まず、呼び出し元に返される値は、一致するcaseブロックの値、つまりブロック内で最後に定義された値です。

2番目に注意することは、呼び出し側が値を取得することを保証する必要があるということです。これを実現するには、whenブロック内のケースが、引数に割り当てることができるすべての可能な値をカバーするようにする必要があります。

** 2.2.

{デフォルトの大文字小文字の区別がある式として\ {}

デフォルトの大文字小文字の区別は、通常の大文字と小文字の区別がない引数値と一致し、Kotlinでは

else

句を使用して宣言されます。いずれにせよ、Kotlinコンパイラはすべての可能な引数値がwhenブロックでカバーされていると仮定し、そうでない場合は不平を言います。

Kotlinの

when

式にデフォルトケースを追加するには

@Test
fun testWhenExpressionWithDefaultCase() {
    val fileType = UnixFileType.L

    val result = when (fileType) {
        UnixFileType.L -> "linking to another file"
        else -> "not a link"
    }

    assertEquals("linking to another file", result)
}


2.3.

例外が発生する

Case__を持つ式

  • Kotlinでは、

    throw



    Nothing .

    ** 型の値を返します。

この場合、

Nothing

は、式が値を計算できなかったことを宣言するために使用されます。

Nothing

は、Kotlinのすべてのユーザー定義型および組み込み型から継承する型です。

したがって、型は

when

ブロックで使用する引数と互換性があるため、

when

ブロックが式として使用されていても

case

から例外をスローすることは完全に有効です。

ケースの1つが例外をスローするwhen式を定義しましょう。

@Test(expected = IllegalArgumentException::class)
fun testWhenExpressionWithThrowException() {
    val fileType = UnixFileType.L

    val result: Boolean = when (fileType) {
        UnixFileType.HYPHEN__MINUS -> true
        else -> throw IllegalArgumentException("Wrong type of file")
    }
}


2.4.

When \ {}

をステートメントとして使用

ステートメントとして

when

ブロックを使用することもできます。

この場合、引数に取り得るすべての値を網羅する必要はなく、各caseブロックで計算された値があっても無視されます。ステートメントとして使用する場合、

when

ブロックは、Javaで

switch

ステートメントを使用する方法と同じように使用できます。

ステートメントとして

when

ブロックを使用しましょう。

@Test
fun testWhenStatement() {
    val fileType = UnixFileType.HYPHEN__MINUS

    when (fileType) {
        UnixFileType.HYPHEN__MINUS -> println("Regular file type")
        UnixFileType.D -> println("Directory file type")
    }
}

例から、

when

をステートメントとして使用している場合、すべての可能な引数値をカバーすることが必須ではないことがわかります。


2.5.

When \ {}

ケースを組み合わせる

Kotlinの

when

式を使用すると、一致条件をコンマで連結することによって、さまざまなケースを1つにまとめることができます。

実行されるコードの各ブロックに対して一致する必要があるのは1つのケースだけなので、コンマは

OR

演算子として機能します。

2つの条件を組み合わせたケースを作成しましょう。

@Test
fun testCaseCombination() {
    val fileType = UnixFileType.D

    val frequentFileType: Boolean = when (fileType) {
        UnixFileType.HYPHEN__MINUS, UnixFileType.D -> true
        else -> false
    }

    assertTrue(frequentFileType)
}


2.6.

{\ {}

が引数なしで使用された場合

Kotlinでは、

when

ブロック内の引数値を省略することができます。

これは基本的に、ケースを順番にチェックして最初に一致したケースのコードブロックを実行する単純な

if-elseif

式の中では有効になります。 whenブロックで引数を省略すると、case式はtrueまたはfalseに評価されます。

引数を省略した

when

ブロックを作成しましょう。

@Test
fun testWhenWithoutArgument() {
    val fileType = UnixFileType.L

    val objectType = when {
        fileType === UnixFileType.L -> "l"
        fileType === UnixFileType.HYPHEN__MINUS -> "-"
        fileType === UnixFileType.D -> "d"
        else -> "unknown file type"
    }

    assertEquals("l", objectType)
}


2.7. 動的ケース式

Javaでは、

switch

ステートメントは、プリミティブとそのボックス型、enum、

Stringクラスでのみ使用できます。これとは対照的に、** Kotlinでは、

when__ブロックを任意の組み込み型またはユーザー定義型と共に使用できます。

さらに、ケースがJavaのように定数式である必要はありません。 Kotlinのケースは、実行時に評価される動的表現にすることができます。たとえば、関数の戻り型が

when

block引数の型と互換性がある限り、caseは関数の結果である可能性があります。

動的なcase式を使って

when

ブロックを定義しましょう。

@Test
fun testDynamicCaseExpression() {
    val unixFile = UnixFile.SymbolicLink(UnixFile.RegularFile("Content"))

    when {
        unixFile.getFileType() == UnixFileType.D -> println("It's a directory!")
        unixFile.getFileType() == UnixFileType.HYPHEN__MINUS -> println("It's a regular file!")
        unixFile.getFileType() == UnixFileType.L -> println("It's a soft link!")
    }
}


2.8. 範囲とコレクションのケース式

与えられたコレクションまたは値の範囲が引数を含んでいるかどうかをチェックする

when

ブロックでケースを定義することは可能です。

このため、Kotlinは

contains()メソッドの構文糖である

in

演算子を提供しています。これは、舞台裏のKotlinがcase要素

in__をcollection.contains(element)に変換することを意味します。

引数がリスト内にあるかどうかを確認するには

@Test
fun testCollectionCaseExpressions() {
    val regularFile = UnixFile.RegularFile("Test Content")
    val symbolicLink = UnixFile.SymbolicLink(regularFile)
    val directory = UnixFile.Directory(listOf(regularFile, symbolicLink))

    val isRegularFileInDirectory = when (regularFile) {
        in directory.children -> true
        else -> false
    }

    val isSymbolicLinkInDirectory = when {
        symbolicLink in directory.children -> true
        else -> false
    }

    assertTrue(isRegularFileInDirectory)
    assertTrue(isSymbolicLinkInDirectory)
}

引数が範囲内にあることを確認するには

@Test
fun testRangeCaseExpressions() {
    val fileType = UnixFileType.HYPHEN__MINUS

    val isCorrectType = when (fileType) {
        in UnixFileType.D..UnixFileType.L -> true
        else -> false
    }

    assertTrue(isCorrectType)
}


REGULAR

FILE

typeが明示的に範囲に含まれていなくても、その序数は

DIRECTORY



SYMBOLIC

LINK

の序数の間にあるため、テストは成功します。


2.9.

Is

ケースオペレータとスマートキャスト

Kotlinの

is

演算子を使用して、引数が指定された型のインスタンスかどうかを確認できます。

is

演算子は、Javaの

instanceof

演算子と似ています。

しかし、Kotlinは「スマートキャスト」と呼ばれる機能を提供しています。引数が与えられた型のインスタンスであるかどうかを調べた後、コンパイラがそれを行うので、引数をその型に明示的にキャストする必要はありません。

したがって、与えられた型で定義されたメソッドとプロパティをケースブロックで直接使用できます。

when

ブロックでis演算子を「スマートキャスト」機能と共に使用するには、次の手順を実行します。

@Test
fun testWhenWithIsOperatorWithSmartCase() {
    val unixFile: UnixFile = UnixFile.RegularFile("Test Content")

    val result = when (unixFile) {
        is UnixFile.RegularFile -> unixFile.content
        is UnixFile.Directory -> unixFile.children.map { it.getFileType() }.joinToString(", ")
        is UnixFile.SymbolicLink -> unixFile.originalFile.getFileType()
    }

    assertEquals("Test Content", result)
}


unixFile



RegularFile



Directory

、または

SymbolicLink

に明示的にキャストしなくても、それぞれ

RegularFile.content



Directory.children

、および

SymbolicLink.originalFile

を使用できました。


3結論

この記事では、Kotlin言語によって提供される

when

ブロックの使用方法の例をいくつか見ました。

Scalaや他のJVM言語の対応する構造体の場合のように、Kotlinで

when

を使用してパターンマッチングを行うことは不可能ですが、

when

ブロックはこれらの機能を完全に忘れるほど多用途です。

この記事の例の完全な実装は、https://github.com/eugenp/tutorials/tree/master/core-kotlin[GitHubに掲載]を参照してください。