Property setter with different type - properties

Considering the following example, is it possible to have a setter for x that accepts an Int and another one that accepts a Double?
class Test(x: Float) {
var x: Float = x
get() {
return field
}
set(value) { // 'value' is of type 'Float'
field = value
}
}
Reason: If I want to assign a new value to x I always have to append the f postfix to every assignment, i.e.
var v = Test(12f)
v.x = 11 // error: 'The integer literal does not conform to the expected type Float'
v.x = 11.0 // error: 'The floating-point literal does not conform to the expected type Float'
v.x = 11f // ok

Although you cannot overload the setter you can take advantage of the Number interface and simply accept all numbers and convert them to floats:
class Test(x: Float) {
var x: Number = x
get() {
return field
}
set(value) { // 'value' is of type 'Number'
field = value.toFloat()
}
}
This means that not only Float, Int, and Double are accepted but also Byte, Short, BigInteger, BigDecimal, AtomicInteger, AtomicLong, AtomicDouble, and any other class that implements Number.

What about:
class Test(x: Float) {
var x: Float = x
get() {
return field
}
set(value) { // 'value' is of type 'Float'
field = value
}
fun setX(value: Int) {
x = value.toFloat()
}
}

Related

Kotlin: Pass ranges in function as arguments

Hello is it possible to pass range in Kotlin function just like in python?
I've just started learning Kotlin but I am a little bit stuck
I wish i could pass somethind like
my_gauge = Gauge('test_name',1..200, 201..300, and etc.)
for example I have a Gauge object which rotates on the base
class Gauge(val gauge_name: String,
val red_value: Float,
val orange_value: Float,
val yellow_value: Float,
val green_value: Float,
var current_value: Float,
val min_value: Float,
val max_value: Float) {
val gauge_green = 0xFF66C2A5.toInt()
val gauge_yellow = 0xFFFDD448.toInt()
val gauge_orange = 0xFFF5A947.toInt()
val gauge_red = 0xFFD53E4F.toInt()
val min_rotation: Int = 0;
val max_rotation: Int = 300;
val ratio = max_rotation / max_value;
fun calculate_rotation(): Int {
return (current_value * ratio).toInt()
}
fun get_color(): Int {
if (current_value >= red_value) {
return gauge_red
}
if (current_value > orange_value) {
return gauge_orange
}
if (current_value > yellow_value) {
return gauge_yellow
}
return gauge_green
}
}
I've just realized that it wont work with this data instead it will be better to build my logic around ranges
So my question is How to pass ranges as a param in class/function (instead of floats)
PS: The function get_colors is not correct I will fix it once I can pass ranges with when(current_value) statement
Yes, the type of a range produced by .. is ClosedRange<T>:
fun foo(floatRange: ClosedRange<Float>) {
println(floatRange.random())
}
// Usage:
foo(1f..10f)
For integer ranges, you may prefer IntRange over ClosedRange<Int> because it allows you to use it without the performance cost of boxing by using first and last instead of start and endInclusive. There is no unboxed version for other number types.
Try this in simple way, you can use range according to data type IntRange, FloatRange, LongRange etc.
fun foo(range: IntRange){
for (a in range){
println(a)
}
}
// call this function by
foo(1..10)

Getting list of enums from bitmask in Kotlin

I have an enum class SettingsVisibility inside an database entity, which I am converting to a bitmask to store in the database. I am trying to reverse the conversion to bitmask, and get a list of enums as a return value. So if I have an enum with values ONE(1), TWO(2), FOUR(4), then it'll store as Enum(7). I want to take 7 and convert it to {ONE, TWO, FOUR}.
My code is below. I have the SettingsVisibility enum with integer values which are stored in the DB. When I try to retrieve from the database, Objectbox will use the given PropertyConvertor to marshall/unmarshall the data. When I want to convertToEntityProperty, it should return a list of just the saved enums, but at the moment it returns a list of all the enums. I can pass a databaseValue of 12 and it will return all enums instead of just 2 (LOCATION AND PAYMENTS).
I think the issue is the usage of enumClass.enumConstants because it gets all the values, but then the filter doesn't work on this, so I am stuck.
#Entity
data class Settings(
#Id override var id: Long = 0,
#Convert(converter = DocumentVisibilityConverter::class, dbType = Int::class)
val showItems: List<SettingsVisibility>
) : Identifiable<Long> {
lateinit var organisation: ToOne<Organisation>
constructor() : this(
showItems = emptyList(),
)
enum class SettingsVisibility(override val bit: Int) : Flags {
USERS(1),
FINANCE(2),
LOCATION(4),
PAYMENTS(8),
MESSAGES(16),
ERRORS(32),
CANCELLATIONS(64)
}
internal class DocumentVisibilityConverter
: BoxConverters.EnumFlagConverter<SettingsVisibility>(SettingsVisibility::class.java)
}
So for example, if I store the first 3, the database value will be 7 (1+2+4).
The database is ObjectBox and here are the property converters:
abstract class EnumFlagConverter<E>(private val enumClass: Class<E>) : PropertyConverter<List<E>, Int> where E : Enum<E>, E : Flags {
override fun convertToDatabaseValue(entityProperty: List<E>?): Int? {
return entityProperty?.toBitMask()?.value
}
override fun convertToEntityProperty(databaseValue: Int?): List<E>? {
return databaseValue?.let(::BitMask)?.enabledValues(enumClass)
}
}
class BitMask(val value: Int)
interface Flags {
val bit: Int
fun toBitMask() = BitMask(bit)
fun <T> BitMask.enabledValues(enumClass: Class<T>): List<T>? where T : Enum<T>, T : Flags? {
return enumClass.enumConstants?.filter(::hasFlag)
}
infix fun <T : Flags?> BitMask.hasFlag(flag: T): Boolean {
if (value == 0 || (value > 0 && flag?.bit == 0)) {
return false
}
return true
}
Maybe the logic in hasFlag is wrong, because I think that just gets every enum if it isn't 0.
Answer was to replace return true, with:
if (flag?.bit?.toByte() == null) {
return false
}
return (this.value.toByte().and(flag.bit.toByte()) == flag.bit.toByte())
This is basically: bit & mask == bit

How to have a custom setter with a named argument and default

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. :)
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.
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.
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.

Type safety in Kotlin doesn't work as expected

I have a code like this:
enum class Player { PLAYER, COMPUTER }
interface BoardCell {
val x: Int
val y: Int
var player: Player?
}
data class Cell(val x: Int, val y: Int, var player: Player?, var value: Int)
data class BoardCellClass(override val x: Int, override val y: Int, override var player: Player?) : BoardCell
data class Request(val board: MutableList<MutableList<BoardCellClass>>? = null, val occupied: MutableList<BoardCellClass>? = null)
class AI(board: MutableList<MutableList<BoardCell>>, private var occupied: MutableList<BoardCell>) {
private var board: MutableList<MutableList<Cell>> = board.map { it.map { Cell(it.x, it.y, it.player, 0) } .toMutableList() } .toMutableList()
}
// in main
val request = call.receive<Request>()
if (request.board == null || request.occupied == null) {
// respond with 403
} else {
val ai = AI(request.board, request.occupied) // Kotlin: Type mismatch: inferred type is MutableList<MutableList<BoardCellClass>>? but MutableList<MutableList<BoardCell>> was expected
// Kotlin: Type mismatch: inferred type is MutableList<BoardCellClass>? but MutableList<BoardCell> was expected
}
But it errors with what is in the comment in the bottom. What am I doing wrong? Clearly, there is an if statement, that catches nullity, so it shouldn't be of type MutableList<MutableList<BoardCellClass>>?, but MutableList<MutableList<BoardCellClass>>, no?
Also, MutableList<MutableList<BoardCellClass>> is compatible with MutableList<MutableList<BoardCell>>, because it implements that interface, right?
MutableList> is compatible with
MutableList>, because it implements that
interface, right?
No. In your case you can use out keyword
class AI(board: MutableList<MutableList<BoardCell>>, private var occupied: MutableList<out BoardCell>) {
private var board: MutableList<MutableList<Cell>> = board.map { it.map { Cell(it.x, it.y, it.player, 0) } .toMutableList() } .toMutableList()
}
Clearly, there is an if statement, that catches nullity, so it
shouldn't be of type
You can write this code in if (not else) part to compiler understand you in wright way
if (request.board != null && request.occupied != null){
val ai = AI(request.board, request.occupied)
} else {
// respond with 403
}
Also, MutableList<MutableList<BoardCellClass>> is compatible with MutableList<MutableList<BoardCell>>, because it implements that interface, right?
No, it isn't. That's an issue of variance. Searching for "Kotlin variance" will give you many explanations, but the simplest way to see why MutableList<BoardCellClass> isn't a subtype of MutableList<BoardCell> is
val list: MutableList<BoardCellClass> = ...
val list1: MutableList<BoardCell> = list // illegal in actual Kotlin!
list1.add(object : BoardCell { ... }) // would add a BoardCell to a MutableList<BoardCellClass>
Then the same logic can be lifted up a level to see that MutableList<MutableList<BoardCellClass>> isn't a subtype of MutableList<MutableList<BoardCell>>.
Clearly, there is an if statement, that catches nullity, so it shouldn't be of type MutableList<MutableList<BoardCellClass>>?, but MutableList<MutableList<BoardCellClass>>, no?
That's not quite how Kotlin smart casts work (though the difference isn't relevant most of the time). It's still of type MutableList<MutableList<BoardCellClass>>?, but if it's used where the required type is MutableList<MutableList<BoardCellClass>>, it's automatically cast. Here the required type is MutableList<MutableList<BoardCell>> instead, and so there's no cast inserted and the nullable type shows up in the error message.

Having a getter return a non-nullable type even though the backing field is nullable

num should be nullable when set, but what it returns should always be non-nullable (have a default value).
class Test {
var num: Int? = null
get() = field ?: 5 // default value if null
}
The following does not compile even though the returned value is always non-null which makes sense to me, because the type is not inferred but taken from the backing field:
val a: Int = Test().num
Type mismatch: inferred type is Int? but Int was expected
The question is how can I change the return type of that getter to be non-nullable? If I do so, the compiler says:
Getter return type must be equal to the type of the property, i.e.
'Int?'
I know that I could solve it with another property numNotNullable (without a backing field).
class Test {
var num: Int? = null
get() = field ?: 5 // default value if null
val numNotNullable: Int
get() = num ?: 5
}
val c: Int = Test().numNotNullable
But this is not what I want. Is there another way?
var num: Int? = null
This is your property signature. It doesn't matter, if you internally ensure that no null value is returned. The signature says, that the value is nullable.
This implicates:
You are allowed to set null to this field
All classes using this field, must handle the fact that the property can return null
Your Solution with a second property is good.
You of course can replace the property with plain old java bean, but I wouldn't advise that, because than you have to access the prop with getNumb and setNum.
class Test {
private var num: Int = 5
fun setNum(num: Int?) {
this.num = num ?: 5
}
fun getNum() = num
}
I don't believe this is possible in Kotlin. You can't override the type of the the property for get/set. So if your property is an Int? you're going to have to return an Int? and check if it is null when you use it.
There's technically a feature request for what you're looking for, but it's been years since it was made.
You can achive this using delegated properties
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class LazyVar<T : Any>(private var initializer: () -> T) : ReadWriteProperty<Any?, T> {
private var value: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
if (value == null) {
value = initializer()
print(value)
}
return value as T
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
class Test {
var num: Int by LazyVar { 5 }
}
val a: Int = Test().num
Note, that this code is not thread-safe. Also with this code sample you can't set null value for you field (so no way back to default value).