此代码如何抛出 NoWhenBranchMatchedException?
How can this code throw a NoWhenBranchMatchedException?
在我们最新的应用程序版本中,我们看到少数 kotlin.NoWhenBranchMatchedException
报告给 Fabric/Crashlytics。
这是有问题的代码片段:
private lateinit var welcome: Welcome
// ...
welcome.welcomeStateLoginStatus.let {
val handled = when (it) {
UnknownUser -> {
btn_login.visibility = View.VISIBLE
btn_logout.visibility = View.GONE
secondButtonFocusedInfoText = getString(R.string.welcome_login_button_info)
tv_user_description.text = null
}
is InternalUser -> {
btn_login.visibility = View.GONE
btn_logout.visibility = View.VISIBLE
secondButtonFocusedInfoText = "Logout"
tv_user_description.text = "Logged in as internal user"
}
ExternalUser -> {
btn_login.visibility = View.GONE
btn_logout.visibility = View.VISIBLE
secondButtonFocusedInfoText = "Logout"
tv_user_description.text = "Logged in as external user"
}
}
}
和 class 定义:
data class Welcome(val welcomeStateLoginStatus: WelcomeStateLoginStatus, val userCanBuySubscription: UserCanBuySubscription? = null) : WelcomeState()
sealed class WelcomeStateLoginStatus() : Serializable
object UnknownUser : WelcomeStateLoginStatus()
data class InternalUser(var user: User) : WelcomeStateLoginStatus()
object ExternalUser : WelcomeStateLoginStatus()
我对这段代码如何 理论上 抛出异常感到困惑 - 如您所见,我们甚至引入了 handled
值只是为了强制编译器确保所有案件都得到处理...
确实是序列化问题:
package com.drei.tv.ui.welcome
import junit.framework.Assert.assertEquals
import org.junit.Test
import java.io.*
class WelcomeStateLoginStatusTest {
@Test
fun testSerialization() {
val original: UnknownUser = UnknownUser
val copy: UnknownUser = unpickle(pickle(original), UnknownUser::class.java)
println("singleton: $UnknownUser")
println("original: $original")
println("copy: $copy")
val handled1 = when (copy) {
original -> println("copy matches original")
else -> println("copy does not match original")
}
val handled2 = when (copy) {
is UnknownUser -> println("copy is an instance of UnknownUser")
else -> println("copy is no instance of UnknownUser")
}
assertEquals(original, copy)
}
private fun <T : Serializable> pickle(obj: T): ByteArray {
val baos = ByteArrayOutputStream()
val oos = ObjectOutputStream(baos)
oos.writeObject(obj)
oos.close()
return baos.toByteArray()
}
private fun <T : Serializable> unpickle(b: ByteArray, cl: Class<T>): T {
val bais = ByteArrayInputStream(b)
val ois = ObjectInputStream(bais)
val o = ois.readObject()
return cl.cast(o)
}
}
产生以下输出:
singleton: com.drei.tv.ui.welcome.UnknownUser@75828a0f
original: com.drei.tv.ui.welcome.UnknownUser@75828a0f
copy: com.drei.tv.ui.welcome.UnknownUser@5f150435
copy does not match original
copy is an instance of UnknownUser
junit.framework.AssertionFailedError:
Expected :com.drei.tv.ui.welcome.UnknownUser@75828a0f
Actual :com.drei.tv.ui.welcome.UnknownUser@5f150435
至于解决方案:要么正确实施Serializable
,要么只使用is
检查而不是等式检查。
感谢 Lionel Briand 和 Hong Duan 为我们指明了正确的方向,感谢 Jason S 在 this answer
中发布了 pickle
和 unpickle
的代码
在我们最新的应用程序版本中,我们看到少数 kotlin.NoWhenBranchMatchedException
报告给 Fabric/Crashlytics。
这是有问题的代码片段:
private lateinit var welcome: Welcome
// ...
welcome.welcomeStateLoginStatus.let {
val handled = when (it) {
UnknownUser -> {
btn_login.visibility = View.VISIBLE
btn_logout.visibility = View.GONE
secondButtonFocusedInfoText = getString(R.string.welcome_login_button_info)
tv_user_description.text = null
}
is InternalUser -> {
btn_login.visibility = View.GONE
btn_logout.visibility = View.VISIBLE
secondButtonFocusedInfoText = "Logout"
tv_user_description.text = "Logged in as internal user"
}
ExternalUser -> {
btn_login.visibility = View.GONE
btn_logout.visibility = View.VISIBLE
secondButtonFocusedInfoText = "Logout"
tv_user_description.text = "Logged in as external user"
}
}
}
和 class 定义:
data class Welcome(val welcomeStateLoginStatus: WelcomeStateLoginStatus, val userCanBuySubscription: UserCanBuySubscription? = null) : WelcomeState()
sealed class WelcomeStateLoginStatus() : Serializable
object UnknownUser : WelcomeStateLoginStatus()
data class InternalUser(var user: User) : WelcomeStateLoginStatus()
object ExternalUser : WelcomeStateLoginStatus()
我对这段代码如何 理论上 抛出异常感到困惑 - 如您所见,我们甚至引入了 handled
值只是为了强制编译器确保所有案件都得到处理...
确实是序列化问题:
package com.drei.tv.ui.welcome
import junit.framework.Assert.assertEquals
import org.junit.Test
import java.io.*
class WelcomeStateLoginStatusTest {
@Test
fun testSerialization() {
val original: UnknownUser = UnknownUser
val copy: UnknownUser = unpickle(pickle(original), UnknownUser::class.java)
println("singleton: $UnknownUser")
println("original: $original")
println("copy: $copy")
val handled1 = when (copy) {
original -> println("copy matches original")
else -> println("copy does not match original")
}
val handled2 = when (copy) {
is UnknownUser -> println("copy is an instance of UnknownUser")
else -> println("copy is no instance of UnknownUser")
}
assertEquals(original, copy)
}
private fun <T : Serializable> pickle(obj: T): ByteArray {
val baos = ByteArrayOutputStream()
val oos = ObjectOutputStream(baos)
oos.writeObject(obj)
oos.close()
return baos.toByteArray()
}
private fun <T : Serializable> unpickle(b: ByteArray, cl: Class<T>): T {
val bais = ByteArrayInputStream(b)
val ois = ObjectInputStream(bais)
val o = ois.readObject()
return cl.cast(o)
}
}
产生以下输出:
singleton: com.drei.tv.ui.welcome.UnknownUser@75828a0f
original: com.drei.tv.ui.welcome.UnknownUser@75828a0f
copy: com.drei.tv.ui.welcome.UnknownUser@5f150435
copy does not match original
copy is an instance of UnknownUser
junit.framework.AssertionFailedError:
Expected :com.drei.tv.ui.welcome.UnknownUser@75828a0f
Actual :com.drei.tv.ui.welcome.UnknownUser@5f150435
至于解决方案:要么正确实施Serializable
,要么只使用is
检查而不是等式检查。
感谢 Lionel Briand 和 Hong Duan 为我们指明了正确的方向,感谢 Jason S 在 this answer
中发布了pickle
和 unpickle
的代码