通用密封类的类型安全用法

时间:2019-02-21 10:03:18

标签: generics kotlin covariance undocumented-behavior sealed-class

编写通用密封类时,我发现了有趣的事情。这是第一个版本:

// sample interface and implementation
interface MyInterface
class MyInterfaceImpl : MyInterface

sealed class Response<T: MyInterface>               

data class Success<T: MyInterface>(val payload: T) : Response<T>()
data class Failure(val errorCode: Int) : Response<MyInterface>()
object Cancelled : Response<MyInterface>()

假设我们也有 request函数,如下所示:

fun <T: MyInterface> requestObject(cls : KClass<T>): Response<T> = TODO("Request")

现在在使用端,我们有错误:

fun test() = when (val response = requestObject(MyInterfaceImpl::class)) {
    is Success -> print("Payload is ${response.payload}")     // Smart cast perfectly works!
    is Failure -> print("Error code ${response.errorCode}")   // Incomparable types: Failure and Response<MyInterfaceImpl>
    Cancelled -> print("Request cancelled")                   // Incomparable types: Cancelled and Response<MyInterfaceImpl>
}
  

第一个问题:对于{em>进/出位置,FailureCancelled并未使用T,这是为什么未选中,我需要抑制它吗?

过一会儿,Konstantin向我展示了如何声明类型安全密封类的解决方案:

sealed class Response<out T: MyInterface>                    // <-- out modifier here

data class Success<T: MyInterface>(val payload: T) : Response<T>()
data class Failure(val errorCode: Int) : Response<Nothing>() // <-- Nothing as type argument
object Cancelled : Response<Nothing>()                       // <-- Nothing as type argument

此声明就像一个咒语,现在我有疑问:

  

第二个问题:为什么需要在此处编写out修饰符?

     

第三个问题:为什么Producer<Nothing>Producer<MyInterface>的子类型?根据协变的定义:如果Producer<A>Producer<B>的子类型,则AB的子类型,但是Nothing不是MyInterface的子类型。看起来像未记录的语言外功能

1 个答案:

答案 0 :(得分:1)

最终无法解决差异。 dates不是numbers,因此无法使用Response<MyInterfaceImpl>Response<MyInterface>。即使您没有使用gerneric类型,您仍然可以声明它。

放置Failure时,在 Java 中,您会得到类似Cancelled的效果。

然后Nothing,您拥有:

  

没有任何实例。您可以使用Nothing来表示“永远不存在的值”。

这也意味着它是所有内容的子类型,因此也适用于泛型。