简体   繁体   中英

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, ie '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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM