https://kotlinlang.org logo
Title
k

Kirill Zhukov

06/10/2019, 3:37 AM
Is there a way to make smart casting work with sealed classes without having to exhaustively handle every case? For instance I expected this to work since I only have two implementations defined for my sealed class.
I could simply add another
is Failure
handler and it doesn’t require to have
else
branch, so the way it works right now seems inconsistent.
More realistic scenario is say I have a sealed class
Result
with only
Success
and
Failure
implementations, and I want combine two different results and basically return failure if one of the results is failure and return success otherwise.
sealed class Result {
  object Success : Result() {
    fun s() {}
  }

  object Failure : Result() {
    fun f() {}
  }
}

val r1: Result = Result.Success
val r2: Result = Result.Failure

// pretend this combines two success results into one final success result
fun success(r1: Result.Success, r2: Result.Success) = Result.Success

val r: Result = when {
  r1 is Result.Failure -> Result.Failure
  r2 is Result.Failure -> Result.Failure
  r1 is Result.Success && r2 is Result.Success -> success(r1, r2) // smart casting works
  else -> { Result.Failure } // shouldn't happen but I have to handle it
}
And here’s how I expected it to work but it doesn’t:
val r: Result = when {
  r1 is Result.Failure -> Result.Failure
  r2 is Result.Failure -> Result.Failure
  else -> success(r1, r2) // can't smart cast
}
d

Dico

06/10/2019, 3:44 AM
Change
is Result.Failure
to `!is Result.Success~
To allow this would make the smart cast extremely implicit and difficult to read
It would also remove the ability to check validity at runtime
The compiler adds an else branch if you exhaust the possible cases, and that branch will throw an error
k

Kirill Zhukov

06/10/2019, 3:46 AM
I guess it’s confusing that this doesn’t require
else
branch, so the compiler does seem to know that there are no more implementations of the class?
val result: Result = Result.Failure
when (result) {
  is Result.Success -> result.s()
  is Result.Failure -> result.f()
}
d

Dico

06/10/2019, 3:46 AM
Yeah
k

Kirill Zhukov

06/10/2019, 3:47 AM
nice, this works:
val r: Result = when {
  r1 !is Result.Success -> Result.Failure
  r2 !is Result.Success -> Result.Failure
  else -> success(r1, r2)
}
d

Dico

06/10/2019, 3:47 AM
You're welcome
k

Kirill Zhukov

06/10/2019, 3:47 AM
Thanks!