52

我有一组共享一些公共字段的数据类,所以理想情况下,我想在超类型中声明它们(本例中为 Message),并且如果他们需要访问这些公共字段,则能够编写对超类型进行操作的函数字段(本例中的 messageId)。

fun operate(m: Message) {
  use(m.messageId)
}

我试图通过从密封类扩展我的数据类来实现这一点。

数据类可以扩展密封类,但我不确定它们如何/是否可以接受“超类型”密封类所需的参数。

  1. 从密封类扩展常规类编译得很好。

    sealed class Message(val messageId: String)
    
    class Track(val event: String, messageId: String): Message(messageId)
    
  2. 但是,将其更改为数据类不会编译(“数据类主构造函数必须只有属性 (val/var) 参数。”)。

    sealed class Message(val messageId: String)
    
    data class Track(val event: String, messageId: String): Message(messageId)
    
  3. 将参数声明为属性也不会编译(“'messageId' 隐藏超类型'Message' 的成员并需要'覆盖'修饰符'”)。

    sealed class Message(val messageId: String)
    
    data class Track(val event: String, val messageId: String): Message(messageId)
    
  4. 打开超类型属性并在每个基类中覆盖它可以很好地编译:

    sealed class Message(open val messageId: String)
    
    data class Track(val event: String, override val messageId: String): Message(messageId)
    

理想情况下,我想要接近选项 2 的东西——它让我能够结合两全其美。

否则,似乎我的选择要么使用选项 1 处理我自己的数据类功能(复制、哈希码、equals 等),要么通过使用选项 4 打开超类型属性来妥协。

4

1 回答 1

80

选项 3 和 4 将导致班级持有messageId两次。一次在新类中,一次在其超类中。

解决方案是在超类中声明但不定义变量:

sealed class Message {
    abstract val messageId: String
}

data class Track(val event: String, override val messageId: String): Message()

这将使 messageId 在 上可用Message,但将存储委托给任何实现它的对象。

于 2017-06-07T18:19:25.377 回答