使用sealed interface配合MappedSuperclass时when语句无法穷尽匹配
问题原因
你遇到的核心问题是编译器无法确定FooItem的所有实现仅为Bar1和Bar2。虽然FooItem是密封接口,但BaseFoo是公开的抽象类——这意味着理论上任何代码都可以继承BaseFoo生成新的FooItem实现,因此编译器判定你的when语句未覆盖所有可能情况,要求添加额外分支。
解决方案
以下两种方案都能实现你的需求:让when语句仅匹配Bar系列类,且新增Bar子类时编译器强制报错。
方案一:将BaseFoo改为密封类(推荐)
JPA允许密封类作为@MappedSuperclass使用,只需修改BaseFoo的定义,就能让编译器识别所有FooItem的实现只能是BaseFoo的子类:
@MappedSuperclass sealed class BaseFoo( // 原有属性 ) : FooItem { // 原有方法 } sealed interface FooItem // 实体类定义保持不变 @Entity @Table(name = "bar_1") class Bar1(...) : BaseFoo(...) { ... } @Entity @Table(name = "bar_2") class Bar2(...) : BaseFoo(...) { ... }
修改后效果:
- 编译器会自动枚举所有
BaseFoo的子类(包括未来新增的BarN),视为FooItem的全部实现 - 你的
when语句仅匹配Bar1、Bar2就会被判定为穷尽,无需额外分支 - 新增
Bar3等子类时,编译器会强制你在when中添加对应分支,否则直接报错
方案二:将FooItem改为密封类
如果不想修改BaseFoo的抽象类属性,可以把FooItem从密封接口改为密封类,让BaseFoo作为它的抽象子类:
sealed class FooItem @MappedSuperclass abstract class BaseFoo( // 原有属性 ) : FooItem() { // 原有方法 } // 实体类定义保持不变 @Entity @Table(name = "bar_1") class Bar1(...) : BaseFoo(...) { ... } @Entity @Table(name = "bar_2") class Bar2(...) : BaseFoo(...) { ... }
此时FooItem是密封类,所有可实例化的实现都是BaseFoo的子类,编译器会自动识别Bar1、Bar2为全部可能情况,同样满足需求。
注意事项
- 确保所有
BaseFoo的子类(实体类)都和BaseFoo在同一模块内,且符合Kotlin密封类的位置规则(同一文件或同一包的子文件,Kotlin 1.5+支持跨文件) - 如果使用Spring Data JPA,Hibernate生成的代理类可能在运行时影响
is判断,可通过Hibernate.unproxy()获取真实实体类型后再执行when匹配(此为运行时问题,不影响编译期的穷尽性检查)
内容的提问来源于stack exchange,提问作者four-eyes
相关产品推荐
相关产品推荐

