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

3

5

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?

Inlay answered 6/10, 2017 at 13:9 Comment(2)
Since you know it's safe, you could technically use .num!!. It's not great, but it's still safe in this case.Tentative
Your backing property solution is definitely the right one, it's even the example for backing properties in the documentation kotlinlang.org/docs/reference/…Reglet
C
5

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
}
Cloudcapped answered 6/10, 2017 at 13:36 Comment(0)
T
5

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.

Toolis answered 6/10, 2017 at 13:28 Comment(0)
C
5

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
}
Cloudcapped answered 6/10, 2017 at 13:36 Comment(0)
E
0

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

Engrail answered 4/5, 2021 at 7:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.