I have the following code example below.
Replacing the following null check + dot action
a != null && a.toInt() == b
with ?.
a?.toInt() == b
"seems" to do the same job and even clearer.
But, as you can see in the image, IDEA doesn't suggest the replacement.
Perhaps the two expressions aren't equivalent?
Example code:
fun main() {
val a: String? = initA()
val b = 1
if (a != null && a.toInt() == b) {
println("true")
} else {
println("false")
}
}
Screencap:
The second I clicked post I understood.
If b's type is changes to nullable, meaning:
val b: Int?
Those expressions cease to be equivalent.
If both a and b are null, the behavior will be different.
Related
is it possible to reformat this piece of code in Kotlin, that it gets a bit smaller?
The function should return A and B as a Pair, if both are unequal to null. Else it should return null. My first idea was like this:
private fun <A, B> zip(a: A?, b: B?): Pair<A, B>? =
if (a != null && b != null)
a to b
else
null
Then I decided to use the Elvis Operator. So it now looks like this:
private fun <A, B> zip(a: A?, b: B?): Pair<A, B>? {
a ?: return null
b ?: return null
return a to b
}
But what I am looking for is just something like this:
private fun <A, B> zip(a: A?, b: B?): Pair<A, B>? =
// This code obviously doesn't compile but is their any way to do it similar to this?
a, b ?: return null
return a to b
Thanks in advance!
One fairly concise option is to create the Pair and then filter it:
(a to b).takeIf{ a != null && b != null }
But this isn't very good: it'll sometimes create a Pair unnecessarily, and the result type will have the Pair params both nullable, even though you know they can't be.
You could write an extension function to make it simpler.
Otherwise, I don't think you can do better than:
if (a != null && b != null) a to b else null
which is slightly longer-winded but has better efficiency and stricter typing.
In the sample below, the function should return a non-null data.
Since the data could be changed in the process, it needs to be var, and can only be nullable to start with.
I can't use lateinit because the first call of if (d == null) will throw.
After the process it will be assigned a non-null data, but the return has to use the !! (double bang or non-null assertion operator).
What is the best approach to avoid the !!?
fun testGetLowest (dataArray: List<Data>) : Data {
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return d!!
}
If you don't like !! then supply a default value for it. You'll realize you can only supply the default value if the list is not empty, but, as you said, the list is already known to be non-empty. The good part of this story is that the type system doesn't track list size so when you say dataArray[0], it will take your word for it.
fun testGetLowest(dataArray: List<Data>) : Data {
var d: Data = dataArray[0]
for (i in 1 until dataArray.size) {
if (d.level < dataArray[i].level) {
d = dataArray[i]
}
}
return d
}
Normally, you can and should lean on the compiler to infer nullability. This is not always possible, and in the contrived example if the inner loop runs but once d is non-null. This is guaranteed to happen if dataArray has at least one member.
Using this knowledge you could refactor the code slightly using require to check the arguments (for at least one member of the array) and checkNotNull to assert the state of the dataArray as a post-condition.
fun testGetLowest (dataArray: List<Data>) : Data {
require(dataArray.size > 0, { "Expected dataArray to have size of at least 1: $dataArray")
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return checkNotNull(d, { "Expected d to be non-null through dataArray having at least one element and d being assigned in first iteration of loop" })
}
Remember you can return the result of a checkNotNull (and similar operators):
val checkedD = checkNotNull(d)
See Google Guava's Preconditions for something similar.
Even if you were to convert it to an Option, you would still have to deal with the case when dataArray is empty and so the value returned is undefined.
If you wanted to make this a complete function instead of throwing an exception, you can return an Option<Data> instead of a Data so that the case of an empty dataArray would return a None and leave it up to the caller to deal with how to handle the sad path.
How to do the same check, and cover the empty case
fun testGetLowest(dataArray: List<Data>)
= dataArray.minBy { it.level } ?: throw AssertionError("List was empty")
This uses the ?: operator to either get the minimum, or if the minimum is null (the list is empty) throws an error instead.
The accepted answer is completly fine but just to mentioned another way to solve your problem by changing one line in your code: return d ?: dataArray[0]
In Kotlin, I want to do an assignment only if another variable is not null (otherwise, no op). I can think of two succinct ways:
fun main(args: Array<String>) {
var x: Int? = null
var n = 0
// ... do something ...
x?.let { n = it } // method 1
n = x ?: n // method 2
}
However, they don't feel succinct enough, given the frequency I have to do them. The first method seems an overkill. The second method is nagging in requiring an expression after ?:.
I suspect there must be a better way, something like n =? x? Or n = x?? Is there?
Try infix to 'simulate custom infix operations'
// define this
infix fun <T > T.assignFromNotNull(right: T): T = right ?: this
///////////////////////////////////////////////////////////
// Demo using
// Now, Kotlin infix-style
fooA assignFromNotNull fooB
barA assignFromNotNull barB
bazA assignFromNotNull bazB
// Old code, Java if-style
if (fooB != null) {
fooA = fooB;
}
if (barB != null) {
barA = barB;
}
if (bazB != null) {
bazA = bazB
}
There's the following:
val x: Int? = null
val n: Int = x ?: return
This compiles perfectly fine, even though n may not be assigned. Even calls that use n after its 'assignment' are allowed, e.g. println(n), because the compiler only knows that n is Int and that's OK. However, any lines following the assignment will never be called, because we return from the scope. Depending on what you want, that's a no-op. We can't continue because n couldn't be assigned, so just return.
Another option is val n: Int = x!! which will throw a NullPointerException if x == null that should be handled elsewhere. I don't recommend this practice, because Kotlin offers cleaner methods to handle nullability.
For simple check like
if (variable != null) {
doSomething(variable)
}
We could change to
variable?.let { doSometing(it) }
However for a case with else
if (variable != null) {
doSomething(variable)
} else {
doOtherThing()
}
Is there a way of doing so in a single function? Is there something like either?
You can use the elvis-operator ?: like so:
variable?.let { doSomething(it) } ?: doOtherThing()
However personally I think that this gets quite unreadable very quickly. There is nothing wrong with an if-expression.
Another approach which might ring well with functional style is the use of when, which is very close to pattern matching:
when(variable) {
null -> doOtherThingSomething()
else -> doSomething(variable)
}
I would argue, however, that you should be careful with abandoning the classic if statement, if what you're after is its natural purpose - conditional execution. If you're calculating a value in two different ways and then using it in the same way, this approach makes sense. But if your code paths diverge at this point and don't meet again, then if provides a clearer intent.
You can map null-able value if not null by using ternary operator to check not null condition with If...Else Statements.
Here, I had wrote some code snippet to check value null or not ,
Case 1: value initialized
fun main(args: Array<String>) {
val value:Int ?= 10
val mapped = value?.let { "Value is == $value" } ?: "Value not initialized..."
println(mapped)
}
You gotta result: Value is == 10
Case 2: value set remains null
fun main(args: Array<String>) {
val value:Int ?= null
val mapped = value?.let { "Value is == $value" } ?: "Value not initialized..."
println(mapped)
}
You gotta result: Value not initialized...
Let f() return a nullable value.
What I want to do is that
if f() is null, get an empty list,
else if f() is not null, get a list of the single item value.
In Scala, we can do something like this:
Option(f()).toList
or more verbosely
Option(f()).map(v => List(v)).getOrElse(List.empty)
In Kotlin, there is no Option (assuming no Funktionale library), and null does not have toList() unlike (None: Option) in Scala.
We have the Elvis operator, but null will be inside the listOf() function, so it will be
listOf(f() ?: /* What can I do here? */)
What we want for null is listOf(/*no argument */), but the Elvis operator requires an argument, so listOf(f() ?: ) will result in a compile error.
At least we can do
val v = f()
if (v == null) listOf() else listOf(v)
but it is a two liner.
Is there some expression for this?
Where I will use this expression is in the class's primary constructor default argument, so if it is not a one liner, it will be enclosed in brackets, so something like this:
class A(
val p1: List<V> = run {
val v = f()
if (v == null) listOf() else listOf(v)
},
val p2: ... = ...,
...)
This looks pretty ugly, isn't it?
EDIT
As #Naetmul pointed out, listOfNotNull(f()) is syntactically better to what I originally posted below, and also takes a variable number of arguments, for example
val myList = listOfNotNull(f(), g(), h())
will return a list of all the results that were not null.
I would use let here.
val myList = f()?.let { listOf(it) } ?: emptyList()
Use a ?. safe call on the return value of f(), then use let to run a code block. If f() is null, it won't run this block of code, resulting in a null value. Then we use the ?: elvis operator to fall back to an empty list.
Here it is broken up into several lines for a better understanding
val myValue = f()
val myList: List<Any>
if (myValue != null) {
myList = listOf(myValue)
} else {
myList = emptyList()
}
For this specific question, I can do
listOfNotNull(f())