Reputation: 35
I have a Kotlin class with named arguments and defaults for non-specified arguments. I am trying to figure out how to create a custom setter for one argument and just can't seem to figure out what I'm doing wrong - although it's probably simple. Here is a simplified version of the class:
class ItemObject(var itemNumber: String = "",
var itemQty: Int = 0)
I can use the properties of this class without issues itemObject.itemQty = itemObject.itemQty + 1 (accessing both the getter and setter).
However, I'd like to make a custom setter to prevent the itemQty from going below zero. So I've tried many variations on the following theme:
class ItemObject(var itemNumber: String = "",
itemQty: Int = 0) {
var itemQty: Int = 0
set(value: Int) =
if (value >= 0) {
itemQty = value // Don't want to go negative
} else {
}
}
This compiles without issue, but seems to keep defaulting itemQty to zero (or something like this - I haven't tracked it down).
Can anyone point me in the correct direction? I'd certainly appreciate it - this is my first Kotlin project and could use a bit of help. :)
Upvotes: 0
Views: 755
Reputation: 29260
This is what vetoable
is for:
var quantity: Int by Delegates.vetoable(0) { _, _, new ->
new >= 0
}
Return true
to accept the value, return false
to reject it.
Upvotes: 3
Reputation: 3453
There are different ways of preventing value going below zero.
One is unsigned types. Experimental at the moment. UInt
in your case
class ItemObject(var itemNumber: String = "",
var itemQty: UInt = 0u
)
If you don't care about value overflow - it might be an option
Another way is Property Delegation
class ItemObject(var itemNumber: String = "", itemQty: Int = 0) {
var itemQty: Int by PositiveDelegate(itemQty)
}
class PositiveDelegate(private var prop: Int) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = prop
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
if (value >= 0) prop = value
}
}
Third one with custom setters is described in other answers.
Upvotes: 0
Reputation: 138247
Well, you initualize the real field to 0 and ignore the passed value ... Instead, initialize the property with the passed constructor parameter:
class Item(_itemQty: Int = 0) {
var itemQty: Int = Math.max(0, _itemQty)
set(v) { field = Math.max(0, v) }
}
(I used two different Identifiers to seperate the parameter and the property, as mentioned in the comments you can also use the same name for the property and the parameter [but be careful, that could add confusion]).
You should also set the backing field, not the setter itself which will end in endless recursion.
If the setter is rather complicated and also needs to be applied to the initial value, then you could initialize the property, and execute the setter afterwards with the parameter:
class Item(_itemQty: Int = 0) {
var itemQty: Int = 0
set(v) { field = Math.max(0, v) }
init { itemQty = _itemQty }
}
As an opinion-based side note, item.itemQty
is not really descriptive, item.quantity
would be way more readable.
Upvotes: 1