我有一个表示字段类型的密封类:

sealed class FieldDef {
    object StringField: FieldDef()
    class ListField(val element: FieldDef): FieldDef()
    class MapField(val children: Map<String, FieldDef>): FieldDef()

    // ... more field types
}

我有一个处理两个字段定义的函数.

    fun processSameTypes(fd1: FieldDef, fd2: FieldDef) {
        if (fd1::class == fd2::class) {
            when (fd1) {
                is MapField -> processMaps(fd1, (fd2 as MapField))
                is ListField -> processLists(fd1, (fd2 as ListField))
            }
        }
    }

编写的代码是正确的-给定逻辑,fd2变量的强制转换将始终成功.

但我觉得应该有一种方法来表达这一点,而不是强制转换,但是如果我试图删除强制转换,我会得到错误:

Kotlin: Type mismatch: inferred type is FieldDef but FieldDef.ListField was expected

有没有更好的方法来处理这个问题,这样我就可以移除石膏(不需要石膏).

如果没有更好的方法解决这个问题,你能解释一下为什么编译器无法推断出fd2变量的正确类型吗?我这里的逻辑是,由于when受外部if保护,并且FieldDef是一个密封的类,编译器应该能够推断出fd2的类型.

Kotlin版本:1.5.30(JVM)

Edit: Conclusion

我认为公平地说,传递类对象是惯用的Java(通过其继承的Kotlin也是如此),特别是它是在运行时表示类型的方式.因此,我希望在将来看到Kotlin编译器支持它.

这就是说,它现在不支持它,所以我同意@Treffnon X的观点--接受它并加入强制转换是最好的解决方案--因此我不打算更改我的代码.

然而,我确实问过一个不使用演员阵容的方法来解决这个问题,@Sweeper提供了一个--所以我会接受这个答案.

推荐答案

不要给when一个参数,这样你就可以判断多个东西的类型.

// Now you can remove "if (fd1::class == fd2::class)" too
when {
    fd1 is FieldDef.MapField && fd2 is FieldDef.MapField -> 
        processMaps(fd1, fd2)
    fd1 is FieldDef.ListField && fd2 is FieldDef.ListField -> 
        processLists(fd1, fd2)
}

或者,创建您自己的内联函数来判断这两个函数:

inline fun <reified TTarget> checkBothTypes(a: Any, b: Any, block: (TTarget, TTarget) -> Unit) {
    if (a is TTarget && b is TTarget) {
        block(a, b)
    }
}

编写此代码的另一种方式是允许从块返回结果:

inline fun <reified TTarget, TResult> checkBothTypes(
    type: KClass<TTarget>, a: Any, b: Any, block: (TTarget, TTarget) -> TResult
): TResult? {
    if (a is TTarget && b is TTarget) {
        return block(a, b)
    }
    return null
}

用途:

checkBothTypes<FieldDef.ListField>(fd1, fd2) { l1, l2 ->
    processLists(l1, l2) // or use a function reference
    // ...
}
checkBothTypes<FieldDef.MapField>(fd1, fd2) { m1, m2 ->
    processMaps(m1, m2) // or use a function reference
    // ...
}

如果希望从所有判断中获得结果,请使用?:将调用链接在一起,从而创建单个表达式.

请注意,与使用when(...)时不同,这些方法无法检测FieldDef的子类的缺失判断.

Kotlin相关问答推荐

为什么";";.equals(1)在柯特林语中是有效的,但";";=1是无效的?

如何在 kotlin 中的数据类中为变量提供多种类型

在Kotlin lambda的参数中如何指定函数类型?

TzdbZoneRulesProvider 在 java.time 中不工作

可以在没有导入声明的情况下调用 Kotlin 扩展函数吗?

init中的NPE抽象函数变量

为什么 trySend 会发出假数据?

在jetpack compose中将默认方向设置为横向?

在 Kotlin 中,我可以在集合上有一个条件构建器元素吗?

SpringBoot 2.5.0 对于 Jackson Kotlin 类的支持,请在类路径中添加com.fasterxml.jackson.module: jackson-module-kotlin

来自类型参数的属性的自定义 getter

如何修复 ViewPager2 中的Design assumption violated错误?

如何使用 Kotlin Coroutines 使 setOnClickListener debounce 1 秒?

主机名不能为空

Kotlin not nullable值可以为null吗?

Kotlin:具有多个不同类型设置器的单个属性

kotlin 委托有什么用?

spring.config.location 在 Spring Boot 2.0.0 M6 上不起作用

kotlin中密封类和密封接口的区别是什么

RxJava2 UndeliverableException 在获取数据时发生方向变化