0

所以我有一个抽象类 Composition,它有两个孩子:一个是 Track,一个是 Album(这是一组 Track)。

class Composition(val name: String, ...)
class Track(name: String): Composition(name)
class Album(name: String, val tracks: List<Track>): Composition(name)

到现在为止还挺好。现在,我有添加的持续时间。它在组合中是抽象的,所以我可以在孩子中覆盖它:

abstract class Composition(...){
    abstract fun getDuration(): Int
}

现在,我可以在 Track 中添加 override 方法,将其作为参数:

class Track(..., private val duration: Int): Composition(...){
    override fun getDuration() = duration
}

最后,我制作了专辑,其持续时间是曲目的总和:

class Album(..., val tracks: List<Track>): Composition(...){
    override fun getDuration() = tracks.sumBy { it.getDuration() }
}

它按预期工作,但我不明白为什么我不能简单地使用tracks.sumBy { it.duration },因为在 Kotlin 中,属性只不过是 getter 和 setter (我正在考虑getDurationin Composition)。

我觉得我遗漏了一些东西,因为如果相同的代码是用 Java 编写的,我将能够composition.duration作为属性调用——所以这让我认为 Kotlin 允许它来自 Java 代码,但不允许来自 Kotlin 代码,这悲伤。

另一个例子:

假设我有一个名为 的类Artist,他写了多个Compositions:

class Artist(
    val nom: String,
    private val _compositions: MutableList<Composition> = ArrayList()
) {

    // HERE (I wrote the extension method List<E>.toImmutableList)
    fun getCompositions() : List<Composition> = _compositions.toImmutableList()
}

这是 Java 中的标准(通过 getter 公开集合的不可变版本,因此它们不会被修改);Kotlin 无法识别它:

val artist = Artist("Mozart")
artist.getCompositions() // Legal
artist.compositions      // Illegal

我考虑过将其设为属性,但是: - 如果我选择类型List<E>,我可以覆盖 getter 以返回不可变列表,但我不能使用常规方法(add...),因为它List是不可变的 - 如果我选择类型MutableList<E>,我无法覆盖 getter 以返回ImmutableList(这是List我编写的子类,显然不是 的子类MutableList)。

当有一个简单的解决方案时,我可能会做一些荒谬的事情,但现在我找不到它。

最后,我的问题是:为什么从 Kotlin 编写的手动编写的 getter 不被视为属性?

而且,如果我弄错了,解决这两种模式的预期方法是什么?

4

2 回答 2

5

您必须将持续时间定义为抽象属性,而不是抽象函数(https://kotlinlang.org/docs/reference/properties.html#getters-and-setters):

abstract class Composition(val name: String) {
    abstract val duration: Int
}
class Track(name: String, override val duration: Int): Composition(name)
class Album(name: String, val tracks: List<Track>): Composition(name) {
    override val duration: Int
        get() = tracks.sumBy { it.duration }
}

作为属性的 getter/setter 转换仅适用于 Java 类 ( https://kotlinlang.org/docs/reference/java-interop.html#getters-and-setters )。

于 2018-11-11T18:18:45.323 回答
5

如果你想将它用作属性,你应该使用 Kotlin-way 来覆盖 getter。
例如:

abstract class Composition(...){
    abstract val duration: Int
}

// You can use "override" in constructor
// val - is immutable property that has only getter so you can just 
// remove private modifier to make possible get it.
class Track(..., override val duration: Int): Composition(...){
    ...
}

class Album(..., val tracks: List<Track>): Composition(...) {
    override val duration: Int 
        get() = tracks.sumBy { it.duration }
}

此外,您可能需要只能在对象内部更改的可变属性。对于这种情况,您可以使用私有 setter 声明可变属性:

class SomeClass(value: Int) {
    var value: Int = value
        private set
}

在文档中阅读更多内容:https ://kotlinlang.org/docs/reference/properties.html#getters-and-setters

于 2018-11-11T18:21:08.640 回答