CLOVIS
CLOVIS

Reputation: 998

Override getters in Kotlin?

So I have an abstract class Composition, which has two children: one is a Track, and one is an Album (which is a group of Tracks).

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

So far, so good. Now, I have the duration that is added. It is abstract in Composition, so I can override it in the children:

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

Now, I can add override the method in the Track, which takes it as a parameter:

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

And finally, I make the Album, whose duration is the sum of the Tracks:

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

It works as intended, but I do not understand why I cannot simply use tracks.sumBy { it.duration }, since in Kotlin properties are nothing more than getters and setters (I'm thinking about the getDuration in Composition).

I feel like I'm missing something, because if the same code was written in Java, I would be able to call composition.duration as a property -- so that makes me think that Kotlin allows it from Java code, but not from Kotlin code, which is sad.

An other example:

Let's say I have a class named Artist, who wrote multiple 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()
}

This is standard in Java (exposing immutable versions of Collections via getters, so they are not modified) ; Kotlin doesn't recognize it though:

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

I thought about making this a property, but: - If I choose the type List<E>, I can override the getter to return the immutable list, but I cannot use regular methods (add...) as the List is immutable - If I choose the type MutableList<E>, I cannot override the getter to return ImmutableList (which is a subclass of List that I wrote, and is obviously not a subclass of MutableList).

There's a chance I'm doing something ridiculous while there is an easy solution, but right now I cannot find it.

In the end, my question is: Why aren't manually-written getters considered properties when written from Kotlin?

And, if I'm mistaking, What is the expected way of solving both of these patterns?

Upvotes: 0

Views: 8485

Answers (2)

osipxd
osipxd

Reputation: 1228

If you want to use it as property, you should use Kotlin-way to override getter.
For example:

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 }
}

Also there are may be case when you need mutable property that can be changed only inside of object. For this case you can declare mutable property with private setter:

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

Read more in docs: https://kotlinlang.org/docs/reference/properties.html#getters-and-setters

Upvotes: 5

Rene
Rene

Reputation: 6148

You have to define duration as an abstract property and not as an abtract function (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 }
}

The getter/setter conversion as properties does only work for Java classes (https://kotlinlang.org/docs/reference/java-interop.html#getters-and-setters).

Upvotes: 5

Related Questions