Kotlinの“ when \ {}”ブロックへのガイド

Kotlinの「when \ {}」ブロックのガイド

1. 前書き

このチュートリアルでは、Kotlin言語のwhen\{}ブロックを紹介し、さまざまな使用方法を示します。

この記事の内容を理解するには、Kotlin言語の基本的な知識が必要です。 言語の詳細については、例のintroduction to the Kotlin Languageの記事を参照してください。

2. Kotlinのwhen\{}ブロック

When\{}ブロックは、基本的にJavaで知られているswitch-caseステートメントの高度な形式です。

Kotlinでは、一致するケースが見つかった場合、それぞれのケースブロックのコードのみが実行され、whenブロックの後の次のステートメントから実行が続行されます。 これは基本的に、各caseブロックの最後にbreakステートメントが必要ないことを意味します。

when\{}の使用法を示すために、Unixのいくつかのファイルタイプのパーミッションフィールドの最初の文字を保持する列挙型クラスを定義しましょう。

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() {
        override fun getFileType(): UnixFileType {
            return UnixFileType.D
        }
    }

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

2.1. 式としてのWhen\{}

Javaのswitchステートメントとの大きな違いは、the when\{} block in Kotlin can be used both as a statement and as an expression. Kotlinが他の関数型言語の原則に従い、フロー制御構造が式であり、それらの評価の結果を呼び出し元に返すことができることです。

返された値が変数に割り当てられている場合、コンパイラは戻り値の型がクライアントが期待する型と互換性があることを確認し、そうでない場合は通知します。

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

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

    assertEquals("d", objectType)
}

whenをKotlinの式として使用する場合、注意すべき点が2つあります。

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

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

2.2. デフォルトの場合の式としてのWhen\{}

デフォルトのケースは、通常のケースと一致しない引数値と一致し、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. When\{} Expression with a Case That Throws an Exception

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. When\{}引数なしで使用

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ステートメントは、プリミティブとそのボックス化された型、列挙型、およびStringクラスでのみ使用できます。 対照的に、Kotlin allows us to use the when block with any built-in or user defined type.

さらに、Javaのようにケースが定数式である必要はありません。 Kotlinのケースは、実行時に評価される動的な式にすることができます。 たとえば、関数の戻り値の型がwhenブロック引数の型と互換性がある限り、ケースは関数の結果である可能性があります。

動的なケース式を使用して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タイプが範囲に明示的に含まれていなくても、その序数はDIRECTORYSYMBOLIC_LINKの序数の間にあるため、テストは成功します。

2.9. Isケース演算子とスマートキャスト

Kotlinのis演算子を使用して、引数が指定された型のインスタンスであるかどうかを確認できます。 is演算子は、Javaのinstanceof演算子に似ています。

ただし、Kotlinは「スマートキャスト」と呼ばれる機能を提供します。 引数が指定された型のインスタンスであるかどうかを確認した後、コンパイラが明示的に引数を型にキャストする必要はありません。

したがって、特定の型で定義されたメソッドとプロパティをcaseブロックで直接使用できます。 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)
}

unixFileRegularFileDirectory、またはSymbolicLinkに明示的にキャストしなくても、RegularFile.contentDirectory.children、およびSymbolicLink.originalFileを使用できました。それぞれ。

3. 結論

この記事では、Kotlin言語が提供するwhenブロックの使用方法の例をいくつか見てきました。

Kotlinではwhenを使用してパターンマッチングを行うことはできませんが、Scalaや他のJVM言語の対応する構造の場合のように、whenブロックは多用途であるため、これらを完全に忘れてしまいます。特徴。

この記事の例の完全な実装は、over on GitHubにあります。