Ръководство за блока „когато {}“ в Kotlin

1. Въведение

Този урок представя блока when {} на език Kotlin и демонстрира различните начини, по които може да се използва.

За да разберете материала в тази статия, са необходими основни познания за езика Kotlin. Можете да разгледате увода към статията за езика Kotlin за Baeldung, за да научите повече за езика.

2. Котлин, когато {} Блокиране

Когато {} блокът е по същество усъвършенствана форма на оператора за превключване, известен от Java.

В Kotlin, ако се намери съвпадащ случай, тогава се изпълнява само кодът в съответния блок case и изпълнението продължава със следващия оператор след блока when . Това по същество означава, че не са необходими оператори за прекъсване в края на всеки случай .

За да демонстрираме използването на когато {} , нека дефинираме enum клас, който съдържа първата буква в полето за разрешения за някои от типовете файлове в 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. Когато {} като израз

А голяма разлика от изявление превключвател Java е, че за когато {} блок в Kotlin може да се използва както като твърдение и като израз. 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.

Първо, стойността, която се връща на повикващия, е стойността на съответстващия блок случай или с други думи последната дефинирана стойност в блока.

Второто нещо, което трябва да забележите, е, че трябва да гарантираме, че повикващият получава стойност. За да се случи това, трябва да гарантираме, че случаите, в блока когато, обхващат всяка възможна стойност, която може да бъде присвоена на аргумента.

2.2. Когато {} като израз със случай по подразбиране

Случаят по подразбиране ще съответства на всяка стойност на аргумент, която не съответства на нормален случай и в Kotlin се декларира с клаузата else . Във всеки случай компилаторът Kotlin ще приеме, че всяка възможна стойност на аргумента е покрита от блока when и ще се оплаче, в случай че не е така.

За да добавите случай по подразбиране в Kotlin когато израз:

@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. Когато {} Израз с дело, което хвърля изключение

В Kotlin хвърля връща стойност от тип Нищо.

В този случай Нищо не се използва за деклариране, че изразът не е успял да изчисли стойност. Нищо не е типът, който наследява от всички дефинирани от потребителя и вградени типове в Kotlin.

Следователно, тъй като типът е съвместим с всеки аргумент, който бихме използвали в блок когато , е напълно валидно да се хвърли изключение от случай, дори когато блокът блок се използва като израз.

Нека дефинираме израз когато, когато един от случаите хвърля изключение:

@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 може да се използва подобно на начина, по който се използва инструкцията за превключване в Java.

Нека използваме блока when като израз:

@Test fun testWhenStatement() { val fileType = UnixFileType.HYPHEN_MINUS when (fileType) { UnixFileType.HYPHEN_MINUS -> println("Regular file type") UnixFileType.D -> println("Directory file type") } } 

От примера можем да видим, че не е задължително да обхващаме всички възможни стойности на аргументи, когато използваме когато като израз.

2.5. Комбиниране на случаите, когато {}

Kotlin е , когато словото ни позволява да се комбинират различни случаи в една от слепване условията на съвпадение със запетая.

Само един случай трябва да съвпада за съответния блок код, който да бъде изпълнен, така че запетая действа като оператор ИЛИ .

Нека създадем случай, който съчетава две условия:

@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, тогава изразите за случай трябва да бъдат оценени или на true, или на false.

Нека създадем една когато блок, който премълчава аргумента:

@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 операторът за превключване може да се използва само с примитиви и техните полета в типове, изброявания и клас String . За разлика от това Kotlin ни позволява да използваме блока when с всеки вграден или дефиниран от потребителя тип.

Освен това не се изисква случаите да са постоянни изрази, както в Java. Случаите в Kotlin могат да бъдат динамични изрази, които се оценяват по време на изпълнение. Например, случаите могат да бъдат резултат от функция, стига типът на връщане на функцията да е съвместим с типа на аргумента, когато блок.

Нека дефинираме блок когато с динамични изрази на регистър:

@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. Обхват и изрази на случаи на събиране

Възможно е да се определи в случай , когато блок, който проверява дали даден събиране или диапазон от стойности съдържа аргумента.

Поради тази причина Kotlin предоставя оператора in , който е синтактична захар за метода contains () . Това означава, че зад кулисите, Kotlin превежда съответното случай елемент в с collection.contains (елемент) .

За да проверите дали аргументът е в списък:

@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 не се съдържа изрично в диапазона, неговият редови номер е между ординалите на DIRECTORY и SYMBOLIC_LINK и следователно тестът е успешен.

2.9. Е Case Operator и Smart Cast

Можем да използваме оператора Kotlin's is , за да проверим дали аргументът е екземпляр от определен тип. В е оператор е подобен на instanceof оператора в Java.

Въпреки това Kotlin ни предоставя функция, наречена „интелигентен актьорски състав“. След като проверим дали аргументът е екземпляр от даден тип, не е нужно да подаваме изрично аргумента към този тип, тъй като компилаторът го прави вместо нас.

Следователно можем да използваме методите и свойствата, дефинирани в дадения тип, директно в блока на случая.

За да използвате оператора 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 съответно.

2.10. когато израз и цикли

От Kotlin 1.4 е възможно прекъсване или продължаване на цикъл дори в израза когато . Например:

val colors = setOf("Red", "Green", "Blue") for (color in colors) { when(color) { "Red" -> break "Green" -> continue "Blue" -> println("This is blue") } }

In the above example, break terminates the nearest enclosing loop and the continue proceeds to the next step, as expected. Before Kotlin 1.4, however, only qualified break and continue were allowed in a when expression inside a loop:

[email protected] for (color in colors) { when(color) { "Red" -> [email protected] "Green" -> [email protected] "Blue" -> println("This is blue") } }

As shown above, the break and continue are qualified with the @LOOP expression.

3. Conclusion

In this article, we have seen several examples of how to use the when block offered by the Kotlin language.

Въпреки че не е възможно да се направи съвпадение на шаблони, използвайки когато в Kotlin, както е в случая със съответните структури в Scala и други JVM езици, блокът when е достатъчно гъвкав, за да ни накара напълно да забравим за тези функции.

Пълното изпълнение на примерите за тази статия може да бъде намерено в GitHub.