如何为 Kotlin 数据模仿或实现 IS-A 关系 类
How to mimic or achieve IS-A relationships for Kotlin data classes
我一直在探索 Kotlin 并写了一个小的 program/script 来完成我觉得很无聊的任务。
在程序开发中我使用数据classes来表示一个Playlist。在设计的某个时刻,我想要一种特殊类型的 Playlist
,即 EmptyPlaylist
。
我无法让它工作。
您将如何与 Kotlin 建立这种关系?
在 Java 中,我会扩展 Playlist
(或者可能创建一个 interface/abstract class 供它们继承)。
我只是想要 List<Playlist>
而不是 List<Playlist?>
最后我只是创建了一个 Playlist
对象,但我感兴趣的是是否可以使用数据 classes.
创建 IS-A 层次结构
UPD:Kotlin Beta 添加了对 data
classes 的限制,因此答案也已更新。
- Data classes cannot be abstract, open, sealed or inner;
- Data classes may not extend other classes (but may implement interfaces).
因此构建层次结构的唯一选择是接口。此限制可能会在未来的版本中解除。
从 Playlist
继承 EmptyPlaylist
的想法根据你的话被认为是非空的看起来有点矛盾,但有选择:
您可以让 TracksPlaylist
和 EmptyPlaylist
实现一些 Playlist
接口。这是合理的,因为 EmptyPlaylist
可能不包含 Playlist
所做的一切。这看起来像:
public interface Playlist
data class TracksPlaylist(val name: String, val tracks: List<Track>) : Playlist
data class EmptyPlaylist(val name: String): Playlist
如果您不想 EmptyPlaylist
的多个实例甚至存在,您可以将其设为 val
而不是不同的 class 并避免 is-检查,只是比较
val EMPTY_PLAYLIST = TracksPlaylist("EMPTY", listOf())
这适用于空播放列表都相等,因此单个对象足以代表它们的情况。
可以使用sealed
classes。这不允许您使用 data
classes,但是 sealed
classes 可能适合构建这样的 class 层次结构。
sealed
class 是一个抽象 class ,它只能在其主体内声明其子class。这让编译器保证这些是唯一的 subclasses。示例:
sealed class Playlist(val name: String) {
class Tracks(name: String, val tracks: List<Track>): Playlist(name)
class Playlists(name: String, val innerPlaylists: List<Playlist>): Playlist(name)
object Empty: Playlist("EMPTY")
}
之后,如果你声明所有的子classes和sealed class的对象,你将可以使用when
语句而不指定else
分支:
when (p) {
is Playlist.Tracks -> { /* ... */ }
is Playlist.Playlists -> { /* ... */ }
Playlist.Empty -> { /* ... */ }
}
但是,这个选项需要你自己处理equals
、hashCode
、toString
、copy
和componentN
,如果你需要的话他们。
我一直在探索 Kotlin 并写了一个小的 program/script 来完成我觉得很无聊的任务。
在程序开发中我使用数据classes来表示一个Playlist。在设计的某个时刻,我想要一种特殊类型的 Playlist
,即 EmptyPlaylist
。
我无法让它工作。
您将如何与 Kotlin 建立这种关系?
在 Java 中,我会扩展 Playlist
(或者可能创建一个 interface/abstract class 供它们继承)。
我只是想要 List<Playlist>
而不是 List<Playlist?>
最后我只是创建了一个 Playlist
对象,但我感兴趣的是是否可以使用数据 classes.
UPD:Kotlin Beta 添加了对 data
classes 的限制,因此答案也已更新。
- Data classes cannot be abstract, open, sealed or inner;
- Data classes may not extend other classes (but may implement interfaces).
因此构建层次结构的唯一选择是接口。此限制可能会在未来的版本中解除。
从
Playlist
继承 EmptyPlaylist
的想法根据你的话被认为是非空的看起来有点矛盾,但有选择:
您可以让
TracksPlaylist
和EmptyPlaylist
实现一些Playlist
接口。这是合理的,因为EmptyPlaylist
可能不包含Playlist
所做的一切。这看起来像:public interface Playlist data class TracksPlaylist(val name: String, val tracks: List<Track>) : Playlist data class EmptyPlaylist(val name: String): Playlist
如果您不想
EmptyPlaylist
的多个实例甚至存在,您可以将其设为val
而不是不同的 class 并避免 is-检查,只是比较val EMPTY_PLAYLIST = TracksPlaylist("EMPTY", listOf())
这适用于空播放列表都相等,因此单个对象足以代表它们的情况。
可以使用
sealed
classes。这不允许您使用data
classes,但是sealed
classes 可能适合构建这样的 class 层次结构。sealed
class 是一个抽象 class ,它只能在其主体内声明其子class。这让编译器保证这些是唯一的 subclasses。示例:sealed class Playlist(val name: String) { class Tracks(name: String, val tracks: List<Track>): Playlist(name) class Playlists(name: String, val innerPlaylists: List<Playlist>): Playlist(name) object Empty: Playlist("EMPTY") }
之后,如果你声明所有的子classes和sealed class的对象,你将可以使用
when
语句而不指定else
分支:when (p) { is Playlist.Tracks -> { /* ... */ } is Playlist.Playlists -> { /* ... */ } Playlist.Empty -> { /* ... */ } }
但是,这个选项需要你自己处理
equals
、hashCode
、toString
、copy
和componentN
,如果你需要的话他们。