【问题标题】:Override getters in Kotlin?覆盖 Kotlin 中的吸气剂?
【发布时间】:2018-11-12 02:28:24
【问题描述】:

所以我有一个抽象类 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)

到目前为止,一切都很好。现在,我有添加的持续时间。它在 Composition 中是抽象的,所以我可以在子项中覆盖它:

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(我正在考虑 Composition 中的 getDuration)。

我觉得我遗漏了一些东西,因为如果相同的代码是用 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&lt;E&gt; 类型,我可以覆盖getter 以返回不可变列表,但我不能使用常规方法(add...),因为List 是不可变的 - 如果我选择MutableList&lt;E&gt;类型,我不能重写getter来返回ImmutableList(这是我写的List的子类,显然不是MutableList的子类)。

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

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

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

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    如果你想将它用作属性,你应该使用 Kotlin 的方式来覆盖 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

    【讨论】:

    • 所以你可以将 vals 覆盖为 vars?
    • 是的,你可以。但并非如此。
    【解决方案2】:

    您必须将持续时间定义为抽象属性,而不是抽象函数 (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)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-16
      • 1970-01-01
      • 2018-04-05
      相关资源
      最近更新 更多