https://kotlinlang.org logo
Title
m

matt tighe

08/29/2019, 6:07 PM
i’ve tried to set up a sealed class that extends an interface in order to get compile-time exhaustiveness, but it doesn’t work the way i wanted it to.
interface ErrorField
data class InputError(
    val field: ErrorField
    val message: String
)

// elsewhere
internal sealed class Field : ErrorField {
    object MyField : Field()
}

// complains about exhaustiveness
return when (error.field) {
    is Field.MyField -> {}
}
is there a way to get the behavior i’m looking for? that is, to have a generally defined type but have the exhaustiveness check be aware that the subtype is actually exhausted?
t

trevjones

08/29/2019, 6:15 PM
the problem is that
InputError.field
is of type
ErrorField
which is what the when statement is considering when checking for exhaustiveness. you might consider doing something like:
kotlin
sealed class ErrorField {
  abstract class SomeBaseType: ErrorField()
  abstract class OtherBaseType: ErrorField()
}
then your when statement would require those two types to be exhaustive:
when(error.field) {
  is ErrorField.SomeBaseType -> ...
  is ErrorField.OtherBaseType -> ...
}
so you can get the compile guarantee by knowing you covered all cases by generalizing them into the super types in the sealed hierarchy.
m

matt tighe

08/29/2019, 6:18 PM
yeah, that’s what i thought. the issue is that i want to have package-specific subtypes and have a “base” living in a higher-level package to define the design. rather than having all possible error fields listed in the base
t

trevjones

08/29/2019, 6:21 PM
with the abstract class you can extend them in the other modules, but it is more rigid being an abstract class than interface
m

matt tighe

08/29/2019, 6:24 PM
i don’t think i’m really understanding you. are you suggesting
sealed class ErrorField {
    abstract class BaseType : ErrorField()
}

// in module
object MyField : ErrorField.BaseType()

// use case
return when (error.field) {
    is MyField -> {}
}
would be exhaustive?
probably some syntax errors in there
t

trevjones

08/29/2019, 6:25 PM
no it would have to be:
when(error.field) {
  is ErrorField.BaseType -> {}
}
but you can short circuit on the specific type before the general type
m

matt tighe

08/29/2019, 6:26 PM
right, but that’s kinda what i’m trying to avoid. i don’t want to have to define the subtypes in the top-level module
t

trevjones

08/29/2019, 6:27 PM
yea in that case it isn’t a fit then. sounds like polymorphism via an interface contract is going to be your best bet
m

matt tighe

08/29/2019, 6:27 PM
could you explain that a bit more?
t

trevjones

08/29/2019, 6:29 PM
you already have the interface
ErrorField
so rather than doing a
when(input.field) { is someType -> doStuff() }
you would just add a function to the interface and call it.
input.field.doStuff()
m

matt tighe

08/29/2019, 6:30 PM
okay, that’s definitely something to consider. thanks for your help!
👍 1
i was able to work something up based on your suggestion that i do think i ultimately like more
😄 1