I'm trying to update a property of a class in plac...
# codereview
a
I'm trying to update a property of a class in place, but the updates are having no effect unless I do it via re-assignment. I know there is a better way but I'm failing to find it...
Copy code
data class counters(var small: Int, var big: Double)
fun Int.add3() : Int = this + 3   // accepts Int, returns Int
fun Double.times2pt5() : Double = this * 2.5 

fun main() {
    val mycounters : counters = counters(4, 201.0)
    mycounters.small.apply{ this.add3() }
    mycounters.apply { this.big.times2pt5() }
    println(mycounters) // prints 4, 201.0 -- same as original
    
    val uglyCounters : counters = counters(6, 401.0)
    uglyCounters.apply {
        this.small = this.small.add3()
        this.big = this.big.times2pt5()
    }
    println(uglyCounters) // works, prints 9, 1002.5, but is ugly
}
j
Do you have some reason to avoid methods on your counters class?
Copy code
data class Counters(var small: Int, var big: Double) {
    fun add3() {
        small += 3
    }
    fun times2pt5() {
        big *= 2.5
    }
I don't really understand the use case, but I'd be looking at instance methods rather than extension functions I think
a
Hi, thanks. The internal classes (small & big) are independent and can live within other superclasses (counters) or even on their own. That's why adding the methods to
counters
is not a good idea, they would have to be replicated for every superclass.
The example is simplified. In reality
small
and
big
are semi-complex objects of their own, not single values. The use case is, I want to manipulate the inner objects using their own methods, and have the outer object be able to reference the inner object post-manipulation.
j
So what's to stop you calling the inner object methods from a method on your
counters
class? Will that cause repeated code or something? A few ways to achieve what you want but it's hard to know what would be most appropriate from your example...
a
The inner objects are their own, independent things. The outer (superclass) object is just a holder or container. An example might be a garage, which contains a car and a truck. You'd call something simple like
car.engine.start()
, and if you later polled
garage.vehicles.started.length
it should return 1 (this is what doesn't happen in to mycounters that I'm trying to fix). Not
garage.engine.start(car)
(because it doesn't make logical sense) and not
garage.apply {car = car.engine.start()}
(i.e. reassignment).
j
What about inspecting the contained objects' state when you want to do your count, something like
Copy code
val garage = Garage()
val car = Car()
val truck = Truck()

garage.add(car)
garage.add(truck)

car.engine.start()

fun Garage.runningVehicles() = getVehicles().filter { it.isStarted() }.size
That way you're using your existing reference to the vehicle and it's going to be easy to maintain. If you really want your Cars not to know about the counter and your counter not to inspect the cars' state, your Garage might need to coordinate your counter class state to accurately reflect the vehicle state, which sounds more error-prone in the longer term, but doable.
a
Yes that is exactly what I am trying to do. However the function signature for start() is
Car -> Car
. Because we didn't re-assign, i.e.
garage.car = car.engine.start()
the garage still contains the old "unstarted" car, thus runningVehicles will return zero.
thanks for the help but we're running in circles I think. I can't change the function signatures, I thought I could update the objects without re-assignment (the uglyCounters example) but perhaps not
👍 1
j
Yeah, if the objects are immutable and you want a referenced object to be updated, I don't think you can get away from some kind of reassignment. Though if your counters class is generic and used in lots of places, containing totally unrelated kinds of object, I don't really understand its value. Sorry not to have any more useful suggestions!