4

Does anybody know a better way to listen for member change within own base class?

class FOO {
    val t: String
}

class BOO: FOO {
    fun x(val t: String) {
        // notify when t was changed 
    }
}

In my opinion JavaRx with Observer would be to much. Kotlin Delegate does not work with inheritance (or i could not find a way yet). The best i came up with is to override setter and getter of "t" in "BOO". But this is a little bit awkward because of this "Why should i overwrite a member?" or "Why do i have to define set and get when i just need set?"

So my solution so far:

import kotlin.properties.Delegates

fun main(args: Array<String>) {
    var foo = FOO()
    foo.t = "foo"
    foo.printT()

    var boo = BOO()
    boo.t = "boo"
    boo.printT()
}

open class FOO () {
    open var t: String? = "origin"
    set(value) {
        println("\t origin setter is called")
        field = String() + value // create a copy (original it is not a String :D)
    }
    get() {
        println("\t origin getter is called")
        return field
    }

    fun printT() = println(t)
}


class BOO (): FOO () {
    override var t: String?
    set(value) {
        // thats all what i need
        println("\t overwritten setter is called")
        super.t = value // execute origin setter
    }
    get() {
        println("\t overwritten getter is called")
        return super.t
    }
}
Mario
  • 758
  • 12
  • 25

3 Answers3

4
open class FOO {
    var t: String = "origin"
        set(value) {
            field = value
            x(t)
        }

    open fun x(t: String) {
        println("FOO: t=$t")
    }
}

open class BOO : FOO() {
    override fun x(t: String) {
        super.x(t)
        println("BOO: t=$t")
    }
}

UPDATE:

I personally think it looks a bit odd but if what you're after is not needing to override any members (field or methods) then you can make your onChange method a property:

open class FOO(val onChange: (t: String) -> Unit = FOO.defaultOnChange) {
    companion object {
        val defaultOnChange: (t: String) -> Unit = { println("FOO: t=$it") }
    }

    var t: String = "origin"
        set(value) {
            field = value
            onChange(t)
        }
}

open class BOO : FOO({ defaultOnChange(it); println("BOO: t=$it") })
mfulton26
  • 29,956
  • 6
  • 64
  • 88
  • @Mario I thought you were trying to avoid needing to override `t`. "Why should i overwrite a member?" Here you don't (unless you're trying to avoid overriding a member method as well). "Why do i have to define set and get when i just need set?" Here you don't, just define set. – mfulton26 Mar 18 '16 at 12:23
  • That seems odd enough to give it a try. I rewrite my classes like this and have a look on the visibility scope of the class objects. – Mario Mar 19 '16 at 01:05
  • Why Unit = FOO.defaultOnChange? - I know the result but I don't know how it works – Arkadiusz Cieśliński Feb 12 '17 at 09:52
  • Correct although it isn't `Unit = ...` but `(t: String) -> Unit = FOO.defaultOnChange`. – mfulton26 Feb 12 '17 at 12:49
2

Similar approach, but with Delegates.observable(), see in Language reference:

import kotlin.properties.Delegates

open class UserBase {
    var name: String by Delegates.observable("<no name>") { prop, old, new ->
        println("$old -> $new")
        onChange(new)
    }

    // could be abstract, if class is abstract
    open fun onChange(v: String?) {}
}

class User : UserBase() {
    override fun onChange(v: String?) {
        println("I was changed in my base class: ${v}")
    }
}

fun main(args: Array<String>) {
    val user = User()
    user.name = "first"
    user.name = "second"
}
Steffen Funke
  • 2,168
  • 1
  • 21
  • 18
  • hi, thanks for your answer. Yes this is the straight forward way and I thought about this as well. But this will increase method count in the api so 3 members will have 3 onChange listener – Mario Mar 18 '16 at 07:53
0

Why not just plain overriding? Like here:

open class FOO () {
    open var t: String = "origin"

    fun printT() = println(t)
}

class BOO (): FOO () {
    override var t: String
    get() = super.t
    set(value) {
        // thats all you need
        println("\t overwritten setter is called")
        super.t = value 
    }
}

The only disadvantage I see here is a need for explicitly overriding the getter, but that is just one extra line.

voddan
  • 31,956
  • 8
  • 77
  • 87
  • Yes... i just added the comments and overwritten getter setter for learning. Anyway what i do not understand why kotlin is forceing me to overwrite the getter... if i do not do so, kotlin will handle the BOO.t in a whole other way then i expect – Mario Mar 21 '16 at 22:04