使用密封类,您可以使用详尽的when
表达式,并在表达式返回结果时省略else
子句:
sealed class SealedClass {
class First : SealedClass()
class Second : SealedClass()
}
fun test(sealedClass: SealedClass) : String =
when (sealedClass) {
is SealedClass.First -> "First"
is SealedClass.Second -> "Second"
}
现在,如果我要向Third
添加SealedClass
,编译器会抱怨when
中的test()
表达式并非详尽无遗,我需要添加一个Third
或else
的条款。
我想知道,当test()
没有返回任何内容时,是否也可以强制执行此检查:
fun test(sealedClass: SealedClass) {
when (sealedClass) {
is SealedClass.First -> doSomething()
is SealedClass.Second -> doSomethingElse()
}
}
如果添加Third
,则此代码段不会中断。
我可以在return
之前添加when
语句,但这很容易被遗忘,如果其中一个子句的返回类型不是Unit
,则可能会中断。
如何确保我不会忘记在我的when
条款中添加分支?
答案 0 :(得分:21)
强制执行详尽when
的方法是使用其值使其成为表达式:
sealed class SealedClass {
class First : SealedClass()
class Second : SealedClass()
class Third : SealedClass()
}
fun test(sealedClass: SealedClass) {
val x = when (sealedClass) {
is SealedClass.First -> doSomething()
is SealedClass.Second -> doSomethingElse()
} // ERROR here
// or
when (sealedClass) {
is SealedClass.First -> doSomething()
is SealedClass.Second -> doSomethingElse()
}.let {} // ERROR here
}
答案 1 :(得分:19)
在Voddan的回答中,您可以构建一个名为function init(){
var button = window.document.createElement("button");
var textNode = window.document.createTextNode("click me");
button.appendChild(textNode);
window.document.body.appendChild(button);
}
的属性,您可以使用:
safe
使用:
val Any?.safe get() = Unit
我认为它提供的信息比仅附加when (sealedClass) {
is SealedClass.First -> doSomething()
is SealedClass.Second -> doSomethingElse()
}.safe
或将结果分配给值更清晰。
Kotlin追踪器上有一个open issue,它会考虑支持“密封的小孩”。
答案 2 :(得分:14)
我们的方法避免在自动完成时将功能放在任何地方。 使用此解决方案,您还可以在编译时使用when返回类型,以便继续使用when返回类型的函数。
Do exhaustive when (sealedClass) {
is SealedClass.First -> doSomething()
is SealedClass.Second -> doSomethingElse()
}
您可以像这样定义此对象:
object Do {
inline infix fun<reified T> exhaustive(any: T?) = any
}
答案 3 :(得分:5)
我们可以在T类型上创建一个扩展属性,其名称有助于说明其目的
val <T> T.exhaustive: T
get() = this
然后在类似
的任何地方使用它when (sealedClass) {
is SealedClass.First -> doSomething()
is SealedClass.Second -> doSomethingElse()
}.exhaustive
这是可读的,准确显示其操作,如果未涵盖所有情况,将显示错误。 Read more here
答案 4 :(得分:1)
一个discussion促使我寻找一种更通用的解决方案,并为Gradle构建找到了一个解决方案。 不需要更改源代码!缺点是编译可能会变得嘈杂。
build.gradle.kts
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
val taskOutput = StringBuilder()
logging.level = LogLevel.INFO
logging.addStandardOutputListener { taskOutput.append(it) }
doLast {
fun CharSequence.hasInfoWithError(): Boolean =
"'when' expression on sealed classes is recommended to be exhaustive" in this
if (taskOutput.hasInfoWithError()) {
throw Exception("kotlinc infos considered as errors found, see compiler output for details.")
}
}
}
build.gradle
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
def taskOutput = new StringBuilder()
logging.level = LogLevel.INFO
logging.addStandardOutputListener(new StandardOutputListener() {
void onOutput(CharSequence text) { taskOutput.append(text) }
})
doLast {
def hasInfoWithError = { CharSequence output ->
output.contains("'when' expression on sealed classes is recommended to be exhaustive")
}
if (hasInfoWithError(taskOutput)) {
throw new Exception("kotlinc infos considered as errors found, see compiler output for details.")
}
}
}
注意:
hasInfoWithError
的实现以推广到其他i:
。subprojects { }
或allprojects { }
中以在整个项目中应用。参考:
kotlinOptions.allWarningsAsErrors
一起可以解决此问题)答案 5 :(得分:1)
考虑使用JakeWharton的最新库,该库允许仅使用@Exhaustive
注释。
sealed class RouletteColor {
object Red : RouletteColor()
object Black : RouletteColor()
object Green : RouletteColor()
}
fun printColor(color: RouletteColor) {
@Exhaustive
when (color) {
RouletteColor.Red -> println("red")
RouletteColor.Black -> println("black")
}
}
用法:
buildscript {
dependencies {
classpath 'app.cash.exhaustive:exhaustive-gradle:0.1.1'
}
repositories {
mavenCentral()
}
}
apply plugin: 'org.jetbrains.kotlin.jvm' // or .android or .multiplatform or .js
apply plugin: 'app.cash.exhaustive'