Anleitung zum Block "when \ {}" in Kotlin

Anleitung zum "when \ {}" -Block in Kotlin

1. Einführung

In diesem Tutorial wird derwhen\{}-Block in Kotlin-Sprache vorgestellt und die verschiedenen Verwendungsmöglichkeiten demonstriert.

Um das Material in diesem Artikel zu verstehen, sind Grundkenntnisse der Kotlin-Sprache erforderlich. Sie können sich den Artikel vonintroduction to the Kotlin Languageam Beispiel ansehen, um mehr über die Sprache zu erfahren.

2. Kotlinswhen\{} Block

Der BlockWhen\{} ist im Wesentlichen eine erweiterte Form der aus Java bekannten Anweisungswitch-case.

Wenn in Kotlin ein übereinstimmender Fall gefunden wird, wird nur der Code im jeweiligen Fallblock ausgeführt und die Ausführung mit der nächsten Anweisung nach dem Blockwhen fortgesetzt. Dies bedeutet im Wesentlichen, dass am Ende jedescase-Blocks keine break-Anweisungen erforderlich sind.

Um die Verwendung vonwhen\{} zu demonstrieren, definieren wir eine Aufzählungsklasse, die den ersten Buchstaben im Berechtigungsfeld für einige der Dateitypen in Unix enthält:

enum class UnixFileType {
    D, HYPHEN_MINUS, L
}

Definieren wir auch eine Hierarchie von Klassen, die die jeweiligen Unix-Dateitypen modellieren:

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\{} als Ausdruck

Ein großer Unterschied zur switch-Anweisung von Java besteht darin, dassthe when\{} block in Kotlin can be used both as a statement and as an expression. Kotlin den Prinzipien anderer funktionaler Sprachen folgt und Flusssteuerungsstrukturen Ausdrücke sind und das Ergebnis ihrer Auswertung an den Aufrufer zurückgegeben werden kann.

Wenn der zurückgegebene Wert einer Variablen zugewiesen wird, prüft der Compiler, ob der Typ des Rückgabewerts mit dem vom Client erwarteten Typ kompatibel ist, und informiert uns, falls dies nicht der Fall ist:

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

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

    assertEquals("d", objectType)
}

Bei der Verwendung von when als Ausdruck in Kotlin sind zwei Dinge zu beachten.

Erstens ist der Wert, der an den Aufrufer zurückgegeben wird, der Wert des übereinstimmenden Fallblocks oder mit anderen Worten der zuletzt definierte Wert im Block.

Als Zweites müssen wir sicherstellen, dass der Anrufer einen Wert erhält. Dazu müssen wir sicherstellen, dass die Fälle im when-Block alle möglichen Werte abdecken, die dem Argument zugewiesen werden können.

2.2. When\{} als Ausdruck mit Standardfall

Ein Standardfall entspricht jedem Argumentwert, der nicht mit einem Normalfall übereinstimmt, und wird in Kotlin mit der Klauselelse deklariert. In jedem Fall geht der Kotlin-Compiler davon aus, dass jeder mögliche Argumentwert vom when-Block abgedeckt wird, und beschwert sich, falls dies nicht der Fall ist.

So fügen Sie einen Standardfall in Kotlinswhen-Ausdruck hinzu:

@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

In Kotlin gibtthrow einen Wert vom TypNothing. zurück

In diesem Fall wirdNothing verwendet, um zu deklarieren, dass der Ausdruck keinen Wert berechnet hat. Nothing ist der Typ, der von allen benutzerdefinierten und integrierten Typen in Kotlin erbt.

Da der Typ mit jedem Argument kompatibel ist, das wir in einemwhen-Block verwenden würden, ist es daher vollkommen gültig, eine Ausnahme von einemcase auszulösen, selbst wenn derwhen-Block als verwendet wird Ein Ausdruck.

Definieren wir einen when-Ausdruck, bei dem einer der Fälle eine Ausnahme auslöst:

@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\{} Wird als Anweisung verwendet

Wir können auch den Blockwhen als Anweisung verwenden.

In diesem Fall müssen wir nicht jeden möglichen Wert für das Argument abdecken, und der in jedem Fall berechnete Wert wird, falls vorhanden, einfach ignoriert. Bei Verwendung als Anweisung kann der Blockwhen ähnlich wie die Anweisungswitchin Java verwendet werden.

Verwenden wir den Blockwhenals Anweisung:

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

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

Wir können dem Beispiel entnehmen, dass es nicht obligatorisch ist, alle möglichen Argumentwerte abzudecken, wenn wirwhen als Anweisung verwenden.

2.5. When\{} Fälle kombinieren

Mit demwhen-Ausdruck von Kotlin können wir verschiedene Fälle zu einem kombinieren, indem wir die Übereinstimmungsbedingungen mit einem Komma verketten.

Nur ein Fall muss mit dem jeweiligen auszuführenden Codeblock übereinstimmen, sodass das Komma alsOR-Operator fungiert.

Erstellen wir einen Fall, der zwei Bedingungen kombiniert:

@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\{} Wird ohne Argument verwendet

Mit Kotlin können wir den Argumentwert im Blockwhenweglassen.

Dies ändert sich im Wesentlichen, wenn in einem einfachenif-elseif-Ausdruck Fälle sequentiell geprüft und der Codeblock des ersten übereinstimmenden Falls ausgeführt werden. Wenn wir das Argument im when-Block weglassen, sollten die case-Ausdrücke entweder true oder false ergeben.

Erstellen wir einenwhen-Block, in dem das Argument weggelassen wird:

@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. Dynamische Fallausdrücke

In Java kann die Anweisungswitchnur mit Grundelementen und ihren Box-Typen, Aufzählungen und der KlasseStringverwendet werden. Im Gegensatz dazuKotlin allows us to use the when block with any built-in or user defined type.

Außerdem ist es nicht erforderlich, dass die Fälle wie in Java konstante Ausdrücke sind. Fälle in Kotlin können dynamische Ausdrücke sein, die zur Laufzeit ausgewertet werden. Beispielsweise können Fälle das Ergebnis einer Funktion sein, solange der Funktionsrückgabetyp mit dem Typ des Blockargumentswhenkompatibel ist.

Definieren wir einenwhen-Block mit dynamischen Fallausdrücken:

@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. Reichweiten- und Sammlungsfallausdrücke

Es ist möglich, einen Fall in einemwhen-Block zu definieren, der prüft, ob eine bestimmte Sammlung oder ein Wertebereich das Argument enthält.

Aus diesem Grund stellt Kotlin den Operatorinbereit, der ein syntaktischer Zucker für die Methodecontains()ist. Dies bedeutet, dass Kotlin hinter den Kulissen das case-Elementin in collection.contains (element) übersetzt.

So überprüfen Sie, ob das Argument in einer Liste enthalten ist:

@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)
}

So überprüfen Sie, ob das Argument in einem Bereich liegt:

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

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

    assertTrue(isCorrectType)
}

Obwohl der TypREGULAR_FILE nicht explizit im Bereich enthalten ist, liegt seine Ordnungszahl zwischen den Ordnungszahlen vonDIRECTORY undSYMBOLIC_LINK, und daher ist der Test erfolgreich.

2.9. Is Case Operator und Smart Cast

Wir können den Operatorisvon Kotlin verwenden, um zu überprüfen, ob das Argument eine Instanz eines bestimmten Typs ist. Der Operatoris ähnelt dem Operatorinstanceof in Java.

Kotlin bietet uns jedoch eine Funktion namens „Smart Cast“. Nachdem wir überprüft haben, ob das Argument eine Instanz eines bestimmten Typs ist, müssen wir das Argument nicht explizit in diesen Typ umwandeln, da der Compiler dies für uns erledigt.

Daher können wir die im gegebenen Typ definierten Methoden und Eigenschaften direkt im case-Block verwenden. So verwenden Sie den Operator is mit der Funktion „Smart Cast“ in einemwhen-Block:

@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)
}

Ohne explizitunixFile inRegularFile,Directory oderSymbolicLink umzuwandeln, konnten wirRegularFile.content,Directory.children undSymbolicLink.originalFile verwenden beziehungsweise.

3. Fazit

In diesem Artikel haben wir einige Beispiele für die Verwendung des von der Kotlin-Sprache angebotenenwhen-Blocks gesehen.

Obwohl es in Kotlin nicht möglich ist, einen Mustervergleich mitwhen durchzuführen, wie dies bei den entsprechenden Strukturen in Scala und anderen JVM-Sprachen der Fall ist, ist der Blockwhen vielseitig genug, um diese vollständig zu vergessen Eigenschaften.

Die vollständige Implementierung der Beispiele für diesen Artikel finden Sie inover on GitHub.