kotlin

Exhaustive when statement with sealed interface not working with MappedSuperclass, Entity and sealed interface


I have this MappedSuperClass

@MappedSuperclass
abstract class BaseFoo(
    ...
) : FooItem {
    ...
}

sealed interface FooItem

Which is then used like this

@Entity
@Table(name = "bar_1")
class Bar1(
    ...
) : BaseFoo(
    ...
) { ... }


@Entity
@Table(name = "bar_2")
class Bar2(
    ...
) : BaseFoo(
    ...
) { ... }


@Entity
@Table(name = "all_bar")
class AllBar(
    ...

    @OneToMany(mappedBy = "all_bar", fetch = FetchType.LAZY, cascade = [CascadeType.REMOVE])
    val bar1: List<Bar1>? = null,

    @OneToMany(mappedBy = "all_bar", fetch = FetchType.LAZY, cascade = [CascadeType.REMOVE])
    val bar2: List<Bar2>? = null,
) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long = 0

    val barItems: List<FooItem>
        get() = listOfNotNull(bar1, bar2).flatten()
}

I then want to add an exhaustive when statement

val bars = allBarRepository.findById(someId).getOrElse {...}
bars.barItems.forEach {
    when (it) {
        is Bar1 -> ...
        is Bar2 -> ...
    }

But the compiler says

'when' expression must be exhaustive, add necessary 'is BaseFoo' branch or 'else' branch instead

I do not want to check for BaseFoo, but only for Bar1 and Bar2 and if there will be more Bar_<INT> for all the other Bars that will be added in the future. I want the compiler to complain when I forget to add any of Bar_<INT> here. By either adding an else branch or checking for BaseFoo this won't work anymore. I thought this is what sealed interfaces can be used for?


Solution

  • BaseFoo should also be sealed.

    sealed class BaseFoo(
        ...
    ) : FooItem {
        ...
    }
    

    sealed classes are implicitly abstract, but classes that are only abstract are not sealed. They "open" the hierarchy, and there could be other classes that inherit from BaseFoo.

    A bit off topic, but Java's sealed interfaces/classes require you to specify whether each subclass is sealed/non-sealed/final. I would recommend keeping that in mind when writing Kotlin too. non-sealed would correspond to having no modifier in Kotlin.