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.
Related
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.
Is it possible to make the following code to compile in Kotlin?
val variable: String? = "string"
val (a, b) = variable?.run {
1 to 2
}
The compiler does not allow destructuring because the expression on the right-hand side is typed as a nullable Pair<Int, Int>?, and it's unclear what values a and b should get in case variable is null.
To solve this, you need to get a not-null expression after =.
There's a lot of different ways to deal with nullable values and produce a not-null value from a nullable one, see: In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them
For example, if you want to provide fallback values for a and b, then use the ?: operator as follows:
val (a, b) = variable?.run {
1 to 2
} ?: (0 to 0)
An alternative, for example, would be to check variable for null first:
val (a, b) = checkNotNull(variable) { "variable should never be null" }.run {
1 to 2
}
Null doesn't have any destructuring declarations. If you want a value of null to destructure like it's a pair of nulls, you could add these extensions:
operator fun <T> Pair<T, *>?.component1() = this?.component1()
operator fun <T> Pair<*, T>?.component2() = this?.component2()
Otherwise, as the other answer shows, you need to provide a default using the Elvis operator.
It's not automatic because it doesn't know what you want. Depending on what you're doing with it, 0 to 0 may be most appropriate, or maybe -1 to -1 or 0 to null or null to null.
The question is, what do you want to do if your variable is null? If you want to throw an exception, go with require or check as #hotkey suggested.
However I have the case where I just want to return if the value is null. So I wrote myself a little helper function that allows for this:
private inline fun <T> T?.exitIfNull(exitBlock: () -> Nothing): T {
if (this == null)
exitBlock()
else
return this
}
You can call this function as follows:
val (a, b) = variable?.run {
1 to 2
}.exitIfNull {
return
}
A nice little use of the Nothing keyword in Kotlin that I personally find quite fascinating
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.
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())
Consider a class with id field which might be null until stored in database:
class IdableK<T : IdableK<T>> : Comparable<T> {
private var id : Long? = null
}
I am trying to implement a compareTo method as follows:
override fun compareTo(other: T): Int {
if (id == null) {
return -1;
}
if (other.id == null) {
return 1;
}
return id!!.compareTo(other.id!!)
}
Is this a correct way of doing it? Would there be a simple way of doing it?
Check out the kotlin.comparisons package. e.g. You can use compareValues:
class IdableK<T : IdableK<T>> : Comparable<T> {
private var id: Long? = null
override fun compareTo(other: T) = compareValues(id, other.id)
}
This is incorrect. If you have two instances with their ids set to null, both instances will return -1 when you call compareTo(other) on them, while if one returns -1 the other should return 1 in a correct implementation. I'm not sure if there are situations where it makes sense to implement compareTo based on nullable properties, but I can't imagine any. Maybe there's a better way for you too?
Also, you should avoid non-null assertions (!!). Since you're using vars, other threads may change the value to null so that even if you did a null check before, the value is now null and !! throws. Instead, you should store both ids in local variables and check these for null values.
If you absolutely have to use compareTo, I'd do it like this:
override fun compareTo(other: T): Int {
val thisId = id
val otherId = other.id
if (thisId == null && otherId == null) return 0
if (thisId == null && otherId != null) return -1
if (thisId != null && otherId == null) return 1
// thisId and otherId are now smart cast to Long
return thisId.compareTo(otherId)
}
Here is a simple way:
override fun compareTo(other: T) :Int {
return id?.compareTo(other.id ?: return 1) ?: -1
}
However this piece of code is very unfriendly to a novice kotlin programmer. It involves too much magic that make it look like scala. These 3 question marks make people puzzled, at least they must think for a minute or two before they could realize what is going on in this minimalistic one-liner. I still prefer your edition. It's more verbose, but clear.
And I'm really worried about the symmetric problem. This matters, and isn't just a design problem. If you don't compare nullable properties, there won't be this programming puzzle. It will just be override fun compareTo(other: T) = id.compareTo(other.id). Simple, clear, and no misleading.
I would rather throw away all null checking code and just live with those null assertions. Because mostly you won't compare there things until it is fully initialized. If these assertion fails, it means something really bad has happens.
Oh, BTW, I don't know about your project, and if it hits the rare cases that you have to compare nullable properties, I think you could write a special edition of Comparator that consider nulls instead of throwing NPEs. Don't mess with the natural order.