How to pattern match optionals in Kotlin? - kotlin

Is it possible to write something like this, or do we have to revert back to manual null checking in Kotlin?
val meaningOfLife : String? = null
when meaningOfLife {
exists -> println(meaningOfLife)
else -> println("There's no meaning")
}

One of possible ways is to match null first so that in else branch the String? is implicitly converted to String:
val meaningOfLife: String? = null
when (meaningOfLife) {
null -> println("There's no meaning")
else -> println(meaningOfLife.toUpperCase()) //non-nullable here
}
This is a special case of a smart cast performed by the compiler.
Similar effect can be achieved with is String and else branches -- is String-check is true when the value is not null.
For more idioms regarding null-safety please see this answer.

You can accomplish that as follows:
val meaningOfLife: String? = null
when (meaningOfLife) {
is String -> println(meaningOfLife)
else -> println("There's no meaning")
}

FYI, the particular situation in the question has a way simple solution with the ?: operator of default value:
println(meaningOfLife ?: "There's no meaning")
The example in the question is probably a simplified real situation, so a null check is how I would do it. IMHO if is a better way to go when you have a binary chose of control flow:
if(meaningOfLife != null)
println(meaningOfLife)
else
println("There's no meaning")
Takes exactly the same number of lines, BTW.

Try this:
val meaningOfLife : String? = null
meaningOfLife?.let { println(it) } ?: println("There's no meaning")
let in stdlib

Related

Kotlin compiler nullability check

I'm a kotlin newby and wondering why the kotlin compiler does not recognize that my variable is never going to be null and how to get around it the nicest way possible.
var myString: String? = null
val myList = mutableListOf<String>()
for (element in 1..2) {
if(myString == null) {
myString = if(*somecondition*) "a" else "b"
}
//compiler error, cannot infer type String? to String
myList.add(myString)
}
A little explaination and how to handle those cases the best way would be nice!
Because String? and String are two different types of variable (or constant). The value of your variable by the time of accessing it might have been changed by another thread/coroutine. In your case i would do something like:
var myString: String? = null
val myList = mutableListOf<String>()
for (element in 1..2) {
myList.add(myString ?: if (/* condition */) "a" else "b")
}
After upgrading to the newest compiler version the code mentioned in the question compiles.

How to use Kotlin to find whether a string is numeric?

I'd like to use a when() expression in Kotlin to return different values from a function. The input is a String, but it might be parsable to an Int, so I'd like to return the parsed Int if possible, or a String if it is not. Since the input is a String, I cannot use the is type check expression.
Is there any idiomatic way to achieve that?
My problem is what the when() expression should look like, not about the return type.
Version 1 (using toIntOrNull and when as requested)
fun String.intOrString(): Any {
val v = toIntOrNull()
return when(v) {
null -> this
else -> v
}
}
"4".intOrString() // 4
"x".intOrString() // x
Version 2 (using toIntOrNull and the elvis operator ?:)
when is actually not the optimal way to handle this, I only used when because you explicitly asked for it. This would be more appropriate:
fun String.intOrString() = toIntOrNull() ?: this
Version 3 (using exception handling):
fun String.intOrString() = try { // returns Any
toInt()
} catch(e: NumberFormatException) {
this
}
The toIntOrNull function in the kotlin.text package (in kotlin-stdlib) is probably what you're looking for:
toIntOrNull
fun String.toIntOrNull(): Int? (source)
Platform and version requirements: Kotlin 1.1
Parses the string as an Int number and returns the result or null if the string is not a valid representation of a number.
fun String.toIntOrNull(radix: Int): Int? (source)
Platform and version requirements: Kotlin 1.1
Parses the string as an Int number and returns the result or null if the string is not a valid representation of a number.
More information: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/to-int-or-null.html
Using let for one
fun isInteger(str: String?) = str?.toIntOrNull()?.let { true } ?: false
Simple and intuitive
fun isNumeric(str: String) = str.all { it in '0'..'9' }
As #coolMind point out, if you want to filter +/-
fun isNumeric(str: String): Boolean = str
.removePrefix("-")
.removePrefix("+")
.all { it in '0'..'9' }
The performance would be similar
If you want to check if it is numeric (Int) the string and do something a simple solution could be:
if (myString.toIntOrNull() != null) {
//Write your code you want to execute if myString is (Int)
} else {
//Write your code you want to execute if myString is (not Int)
}
Sharing Regex matches solution, repost from my answer here
Best suited solution if negative and positive number which can be formatted with '-' and '.'
below method returns true if formatted string number matches the regex pattern
fun isPosOrNegNumber(s: String?) : Boolean {
val regex = """^(-)?[0-9]{0,}((\.){1}[0-9]{1,}){0,1}$""".toRegex()
return if (s.isNullOrEmpty()) false
else regex.matches(s)
}
Above sample regex is only for US number formats but if you want to use EU number formats then just replace '.' with ',' in regex pattern string
Note:. if the numbers contain commas then just replace it while sending to this method or better form a regex pattern with commas in it.
I searched for the same and I found this answer so I have made my own version from the above answer:
//function to check strin is int or bull
fun String.intOrString(): Boolean{
val v = toIntOrNull()
return when(v) {
null -> false
else -> true
}
}

Kotlin nullability check if-else functional approach...How?

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

null to listOf(), not null to listOf(value) in Kotlin in one liner?

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

Comparing two optionals in Kotlin

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.