I have this code - which can't smart cast in when ...
# getting-started
a
I have this code - which can't smart cast in when block, Is this not allowed by design?
Copy code
sealed interface  UserAccountInfo {
    /** No user info, no token */
    object UnAuthenticated : UserAccountInfo

    /** No token info */
    data class LoggedOut(val userInfo: String) : UserAccountInfo

    /** User info is present, token is expired */
    data class HasExpiredToken(val userInfo: String,
                               val token: String,
                               val userId: String) : UserAccountInfo

    /** User info and token are present, token might be expired */
    data class LoggedIn(val userInfo: String,
                        val token: String,
                        val userId: String): UserAccountInfo
             
}

 
fun main() {
    val userAccountInfo: UserAccountInfo = UserAccountInfo.UnAuthenticated
        when (userAccountInfo) {
            is UserAccountInfo.HasExpiredToken, is UserAccountInfo.LoggedIn -> {
                val token = userAccountInfo.token
                val userInfo = userAccountInfo.userInfo
                val userId = userAccountInfo.userId
            }
            else -> {
                println("Can not refresh token due to lack of authentication data")
            }
        }
}
Gives
Unresolved reference: token
error. But if I repeat code, it can work
Copy code
is UserAccountInfo.HasExpiredToken -> {
                val token = userAccountInfo.token
                val userInfo = userAccountInfo.userInfo
                val userId = userAccountInfo.userId
            }
            is UserAccountInfo.LoggedIn  -> {
                val token = userAccountInfo.token
                val userInfo = userAccountInfo.userInfo
                val userId = userAccountInfo.userId
            }
Is there a way to remove code duplication? https://pl.kotl.in/eT1mSAOfk
a
you could create another interface (sealed, if you prefer), e.g.
Copy code
sealed interface UserAccountInfo {
  sealed interface WithToken { val token: String }
}
and implement
WithToken
in
HasExpiredToken
and
LoggedIn
, and then in your when-statement, check for
is UserAccountInfo.WithToken -> {}
because currently, what should the type of
x
be?
Copy code
when (userAccountInfo) {
    is UserAccountInfo.HasExpiredToken, is UserAccountInfo.LoggedIn -> {
        val x: TYPE= userAccountInfo
    }
Maybe one day we will have union types…
Copy code
val x: UserAccountInfo.HasExpiredToken | UserAccountInfo.LoggedIn= userAccountInfo
But not yet.
w
To answer your question; yes. This is by design. There is no duck-typing in Kotlin. Two types might have the same field or method, but that does not mean that they imply the same behavior. To imply that 2 types share the same behavior, they need to implement an interface that defines the common behavior, such as @Adam S suggested.
@Adam S, the purpose of union types is not solve this problem. There is no ticket I am aware of that will introduce this. You can create a ticket (and supply use-cases to improve your odds) if you think this would be a valuable addition to Kotlin.
a
Thanks, will try with embedded - WithToken