assign variable only if it is null - kotlin

on Ruby one have something like this:
#var ||= 'value'
basically, it means that #var will be assigned 'value' only if #var is not assigned yet (e.g. if #var is nil)
I'm looking for the same on Kotlin, but so far, the closest thing would be the elvis operator. Is there something like that and I missed the documentation?

The shortest way I can think of is indeed using the elvis operator:
value = value ?: newValue
If you do this often, an alternative is to use a delegated property, which only stores the value if its null:
class Once<T> {
private var value: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? {
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
this.value = this.value ?: value
}
}
You can now create a property that uses this like so:
var value by Once<String>()
fun main(args: Array<String>) {
println(value) // 'null'
value = "1"
println(value) // '1'
value = "2"
println(value) // '1'
}
Note that this is not thread-safe and does not allow setting back to null. Also, this does evaluate the new expression while the simple elvis operator version might not.

Other way we could do it is by using ifEmpty..
From the docs: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/if-empty.html
Usage:
val empty = ""
val emptyOrNull: String? = empty.ifEmpty { null }
println(emptyOrNull) // null
val emptyOrDefault = empty.ifEmpty { "default" }
println(emptyOrDefault) // default
val nonEmpty = "abc"
val sameString = nonEmpty.ifEmpty { "def" }
println(sameString) // abc
EDIT:
Seems like this does not work if the initial value is NULL and only for strings..

Related

Kotlin non-null assertion with safe operator

Why this code is giving me the NPE error?
fun main() {
val myObj: MyObj? = null
print(myObj?.someVal!!)
}
class MyObj{
val someVal = 1
}
Does non-null assertion evaluate the whole expression before? I thought that myObj? should be enough to print null here.
Yes. When you use a null-safe call, the expression is evaluated to null if the receiver is null, so
myObj?.someVal!!
behaves the same as
(if (myObj != null) myObj.someVal else null)!!
(If myObj is a property rather than local variable, these aren't equivalent, since smart-casting wouldn't work.)
Simple example:
This code will print null:
fun main() {
val myObj: MyObj? = null
val someVal = myObj?.someVal
print(someVal)
}
This code will throw NPE as the variable is null:
fun main() {
val myObj: MyObj? = null
val someVal = myObj?.someVal
print(someVal!!)
}

Is there a way to distinguish between a function argument's default value having been passed explicitly or implicitly in Kotlin?

Assuming a kotlin function like this:
fun f(p1: T1? = null, p2: T2? = null, ..., pN: TN? = null) {
// ...
}
Can the above function's implementation distinguish between the following two calls, where the first one passed p1 = null implicitly, and the second one passed it explicitly?
f() // Implicit
f(null) // Explicit
f(p1 = null) // Explicit
Note: There could be arbitrary numbers of parameters
No, it cannot distinguish between those cases.
You could distinguish between them if you added a distinct overload, however.
Although I'd rather not use that approach in production, you could do something like I've done in the following snippet:
object Default {
val defaultMapping = mutableMapOf<KClass<*>, Any?>()
inline fun <reified T> get(): T? =
T::class.let {
defaultMapping[it] ?: it.java.constructors.getOrNull(0)?.let { c ->
try {
// NOTE: for now only parameterles constructor will work
c.newInstance()
} catch (e: Exception) {
e.printStackTrace()
null
}.also { v ->
defaultMapping[it] = v
}
} ?: run {
defaultMapping[it] = null
null
}
} as? T
inline fun <reified T> T.isDefault(): Boolean = defaultMapping[T::class] == this
}
inline fun <reified T> foo(bar: T? = Default.get()) {
if (bar?.isDefault() == true) println("bar: default is in use")
else println("bar: $bar")
}
fun main() {
foo<Any>()
foo(Default.get<Any>())
foo<Any>(null)
foo<Any>(bar = null)
foo(Any())
val a = Any()
foo(a)
foo(bar = a)
}
Note, that I have not polished the code in any way. Some parts are leftovers from several attempts (e.g. the part about the constructors.getOrNull(0)) and I don't intend to improve that.
Also: This simple approach only works with default constructors (see it.newInstance()) on the JVM. So that's no multi-platform solution in any way.
The result is something like
bar: default is in use
bar: default is in use
bar: null
bar: null
bar: java.lang.Object#41906a77
bar: java.lang.Object#4b9af9a9
bar: java.lang.Object#4b9af9a9
Again: Keep in mind, this is very simplistic, don't use that in production!

How to use get() with backing fields in kotlin

in the below code I am trying to create a getter method as a backing field.
so that, when the getLastNameLen property is invoked it should return the length of the lastNameset.
please refer to the code below and help me to fix the bug.
how to display output of the backing fields
code:
class Thomas (val nickname: String?, val age : Int?) {
//backing field 1
var lastName : String? = null
set(value) {
if (value?.length == 0) throw IllegalArgumentException("negative values are not allowed")
field = value
println("lastname backing field set: ${field} ")
}
val getLastNameLen
get() = {
this.lastName?.length
}
}
output
lastname backing field set: jr.stephan
lastName is jr.stephan
lastNameLen is () -> kotlin.Int?
This is because you are using the = operator which is setting the getter to be a lambda.
You have two options:
val getLastNameLen
get() {
return this.lastName?.length
}
OR
val getLastNameLen
get() = this.lastName?.length
basically use the brackets right after get() to make a getter function, or if you can do it in one line use an = right after the get() but don't include the {} otherwise it will treat it like its a lambda

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

Property delegation baked by mutable map

I have a following code:
class Mapped(var data:Map<String,String>){
val firstName:String by data
}
This works fine in case the Mapped is used as follows:
val mapped = Mapped(mapOf("firstName" to "initialFirstName"))
println(mapped.firstName); // prints "initialFirstName"
However since the data property is mutable we can change it's value i.e.:
mapped.data = mapOf("firstName" to "updated");
However the firstName property still holds the "initialFirstName".
Is there a work around to this, known/documented albeit unexpected (to me) behavior?
Until the issues KT-5870, KT-9772 are resolved you can do the following:
operator fun <V, V1 : V> (() -> Map<in String, V>).getValue(thisRef: Any?, property: KProperty<*>): V1 {
val map = this()
return map[property.name] as V1
}
Which can then be used as follows:
class Mapped(var data:Map<String,String>){
val firstName:String by { data }
}
The above does not handle nullability well. Here's an improved version:
operator fun <V, V1 : V> (() -> Map<in String, V>).getValue(thisRef: Any?, property: KProperty<*>): V1 {
val map = this()
val key = property.name
#Suppress("UNCHECKED_CAST")
val value = map[key] as V1
if (property.returnType.isMarkedNullable) {
return value
} else {
if(value != null){
return value
}
if(map.containsKey(key)){
throw KotlinNullPointerException("Property baking map returned null value for key '$key' for non nullable property: $property")
} else {
throw KotlinNullPointerException("Property baking map has no key '$key' for non nullable property $property")
}
}
}