A Setter Improvement

Regarding getters and setters, I was implementing a class property that counts how many times it was accessed. Here’s my starting code for that:

class AccessCounter {
    var count: Int = RESET_VALUE  // 0
        get() = ++field
        // Setter in the next step
}

Now, I’d like to be able to set it according to various options like “RESET”, “MID”, “MAX”, and so on. Here’s what that would look like (gives an error):

class AccessCounter {
    var count: Int = RESET_VALUE  // 0
        get() = ++field
        set(v: String) {  // Error (new feature)
            when(v) {
                "RESET" -> field = RESET_VALUE
                "MID" -> field = MID_VALUE  // Some Int value
                "MAX" -> field = MAX_VALUE  // Some Int value
                // And so on
            }
        }
}

fun main() {
    val ac = AccessCounter()
    // Some code here which gets the counter multiple times (thus adding to it the number of times it was accessed)
    println(ac.count)  // Returns an Int with the number of times count was accessed (including this time)
    ac.count = "RESET" // Error (new feature)
}

The problem with this is that “RESET” is a String, and only Ints can be passed to the setter parameter, since count is an Int.

Would it be possible to have a setter parameter of a type other than the property type, like in this case? If we don’t specify the type, Kotlin infers the property type like it does so far (to Int in this case), but if we specify another type it still accepts it (the field would still be updated to the property type, like in the example above).

Extra

There’s of course a turnaround in which we can pass integers to the setter, each corresponding to a specific String option (like below), but maybe this could be avoided:

class AccessCounter {
    var count: Int = RESET_VALUE  // 0
        get() = ++field
        set(v) {
            when(v) {
                0 -> field = RESET_VALUE
                1 -> field = MID_VALUE  // Some Int value
                2 -> field = MAX_VALUE  // Some Int value
                // And so on
      }
    }
}

fun main() {
    val ac = AccessCounter()
    // Some code
    ac.count = 1  // MID_VALUE
    // Some code
    ac.count = 0  // RESET_VALUE
}

Thank you for your attention and support!

*Also, happy new year!

Firstly, instead of using Strings or magic Ints, I would recommend using Enums.
There has been some talk about allowing property setters to have a different type, mostly in this KEEP, but that doesn’t currently exist. However, I think for your case, it really doesn’t make sense to use the option on count; it reads strangely. Instead, I would do something like:

import CounterOptions.*
enum class CounterOptions {
  RESET, MID, MAX
}
class AccessCounter {
    var count: Int = 0
        get() = ++field
        private set
    fun resetCount(v: CounterOptions) {
        count = when(v) {
            RESET -> 0
            MID -> 50  // Some Int value
            MAX -> 100  // Some Int value
            // And so on
      }
    }
}

fun main() {
    val ac = AccessCounter()
    println(ac.count)
    ac.resetCount(RESET)
    println(ac.count)
    ac.resetCount(MID)
    println(ac.count)
    ac.resetCount(MAX)
    println(ac.count)
}
2 Likes

Similar to @kyay10’s answer, but using delegates to be more similar to the original proposal using getters:

import kotlin.reflect.KProperty

class Counter {
	var count: Int = 0
		get() = ++field
		private set

	fun reset() {
		count = 0
	}

	fun mid() {
		count = 50
	}

	fun max() {
		count = 100
	}

	operator fun provideDelegate(thisRef: Any?, property: KProperty<*>) = ::count
}

fun main() {
	val counterControl = Counter()
	val counter by counterControl

	println(counter)
	counterControl.reset()
	println(counter)
	counterControl.mid()
	println(counter)
	counterControl.max()
	println(counter)
}
1 Like

I think this is not a great idea because it flies in the face of common convention. The obvious one is that having a getter modifying the value is generally frowned upon for good reason : anyone using your code will typically be puzzled by this behavior.

As for the setter, as has been pointed out the magic string values won’t serve you very well : typing mistakes, no code completion etc.

Instead I would suggest simply using methods. Why not make your property of type AccessCounter and have it offer methods like incrementAndGet(), reset(), mid(), max() ?

Also possibly consider just using kotlin.concurrent.AtomicInt. The obvious drawback is that it offers some additional methods that you may not want to offer. But :

  • It will play nice with multithreaded cases (which your current code doesn’t).
  • It already has incrementAndGet().
  • You can write extension methods on it for what you want to add (e.g. fun AtomicInt.reset() = getAndSet(RESET_VALUE)).
1 Like