Kotlin and ?.let: how to choose variants of variable's fields? - kotlin

I have a class:
class Clazz(
val name: String?,
val value0: String,
val value1: String?,
val value2: String
)
I want to print value0 if we have non-null name. But if name is null, then i want return value1 only if it is not null. if it is, return value2.
i do it in let way:
val result =
clazz.name?.let {
return clazz.value0
} ?: run {
clazz.value1 ?: clazz.value2
}
but i think it is quite shitty code and it could be done better and simplier.
Is it such a way?

Try below code it will work:
Code:
val result: String = when {
clazz.name.isNullOrEmpty() -> {
clazz.value1 ?: clazz.value2
}
else -> {
clazz.value0
}
}
Explanation:
val result: String = when {
// check name is null --> true
clazz.name.isNullOrEmpty() -> {
// check value1 is not null, if null then return value2
clazz.value1 ?: clazz.value2
}
else -> { // name is not nul so it will return value0
clazz.value0
}
}

You basically have a situation where you have a list of alternatives (value0, value1, value2), and you want to pick the first alternative that is non null. value0 doesn't quite fit into this, but we can make it fit by using ?.let as in your attempt, or in general, make any condition fit using takeIf.
Then you can just write your alternatives and separate them with the elvis operator ?::
clazz.value0.takeIf { clazz.name != null } ?: // or clazz.name?.let { clazz.value0 }
clazz.value1 ?:
clazz.value2
You can also extract out clazz into a run:
clazz.run {
value0.takeIf { name != null } ?:
value1 ?:
value2
}
This reads just like English:
Take value0 if name is not null, otherwise value1 if it is not null. Otherwise value2.

In Kotlin, if can be used as an expression (like when). And it's often more readable than ?.let {..} ?: run {..}. You could use it this way:
val result = if (clazz.name != null) {
clazz.value0
} else {
clazz.value1 ?: clazz.value2
}
Now, as is often the case, isolated pieces of logic like this are best extracted into a separate function. So I would suggest extracting this piece in an extension function on Clazz:
val result = clazz.meaningfulNameForThis()
private fun Clazz.meaningfulNameForThis() = if (name != null) {
value0
} else {
value1 ?: value2
}

Related

Kotlin, how can I make my code shorter in this spaghetti code?

I'm working on readlines now and can I make this few if's shorter? I'm making a validation to what user is sending to me. The filed cant be empty or null. I have 3 important things that user has to write in field and every three times I have to check the same... .
fun readlinesToAddEntryAndValidation(): List<String> {
println(ENTER_DESCRIPTION_ID_TEKST)
val entryId: String? = readLine()
if (!entryId.isNullOrEmpty()) {
println(ENTER_DESCRIPTION_NAME_TEKST)
val name: String? = readLine()
if (!name.isNullOrEmpty()) {
println(ENTER_DESCRIPTION_TEKST_TEKST)
val tekst: String? = readLine()
if (!tekst.isNullOrEmpty()) {
return listOf(entryId, name, tekst)
} else {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
} else {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
} else {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
}
Try to avoid cognitive complexity one of the things is avoid nesting. Also when an if always returns something. An else statement is not needed
fun readlinesToAddEntryAndValidation(): List<String> {
println(ENTER_DESCRIPTION_ID_TEKST)
val entryId: String? = readLine()
if (entryId.isNullOrEmpty()) {
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
println(ENTER_DESCRIPTION_NAME_TEKST)
val name: String? = readLine()
if (!name.isNullOrEmpty()) {
println(ENTER_DESCRIPTION_TEKST_TEKST)
val tekst: String? = readLine()
if (!tekst.isNullOrEmpty()) {
return listOf(entryId, name, tekst)
}
}
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
You could do something like this:
fun readlinesToAddEntryAndValidation() : List<String> {
fun read(message: String): String? {
println(message)
val line = readLine()
return if (line.isNullOrEmpty()) null else line
}
read(ENTER_DESCRIPTION_ID_TEKST)?.let { entryId ->
read(ENTER_DESCRIPTION_NAME_TEKST)?.let { name ->
read(ENTER_DESCRIPTION_TEKST_TEKST)?.let { tekst ->
return listOf(entryId, name, tekst)
}
}
}
println(EMPTY_READLINE_ERROR)
return readlinesToAddEntryAndValidation()
}
I wouldn't normally recommend nesting too much, but I feel like that's fairly readable with only three parameters, and the null checking means it short-circuits as soon as you run into a problem.
Making user to reenter all previous (independent!) values after his mistake in the middle of the input is a bad UI.
If user failed to correctly input some entry, you need to ask him to reenter only this single item (until he eventually do it right):
fun read(inputMessage: String, errorMessage: String = EMPTY_READLINE_ERROR): String {
println(inputMessage)
var line: String? = readLine()
while (line.isNullOrEmpty()) {
println(errorMessage)
println(inputMessage)
line = readLine()
}
return line
}
With this auxilary function, whole program become a single-liner:
fun readlinesToAddEntryAndValidation() =
listOf(
ENTER_DESCRIPTION_ID_TEKST,
ENTER_DESCRIPTION_NAME_TEKST,
ENTER_DESCRIPTION_TEKST_TEKST
).map { read(it) }

How and when does kotlin let run?

for all the examples on the internet i cant figure out when and how is kotlins let ran?
if(phones.size == 0){
phones.add("")
}
return phones[0]
so if phones list size is 0, we add empty string and return that instead.
Now how would one do same with let ?
phones.let {
return ""
}
does this work with size 0, or do i have to have null list?
do i need return keyword, if yes, where?
is the above fun always going to return empty string? or just when phones is null?
when is this let code block even ran?
Update:
val cakes = listOf("carrot", "cheese", "chocolate")
fun main(args: Array<String>) {
var cakesEaten = 0
while (cakesEaten < 3) { // 1
cakesEaten ++
val result = cakes?.let{
if(cakesEaten == 2) {
"HeyLo"
} else {
2
}
}
println("result value = $result")
when(result) {
is String -> println(" result variable is a String")
is Int -> println(" result variable is Integer")
}
}
}
result value = 2
result variable is Integer
result value = HeyLo
result variable is a String
result value = 2
result variable is Integer
Original post
If your 'phones' Object is a Nullable type,
val result = phones?.let{
// this block runs only if phones object is not null
// items can be accessed like it.size
// expression result will be returned. no need to mention return.
if(it.size == 0) {
it.add("")
it[0]
} else it.size
}
result value will be either it[0] or it.size and its type will be Any.
But if this the functionality you need you can check Markos solution.
If you're interested in how to write your logic in Kotlin's FP idiom, it doesn't involve let at all:
phones.takeIf { it.isEmpty() }?.add("")
return phones[0]
However, I don't find this idiom better than what you started out with.

How to convert String to Int in Kotlin?

I am working on a console application in Kotlin where I accept multiple arguments in main() function
fun main(args: Array<String>) {
// validation & String to Integer conversion
}
I want to check whether the String is a valid integer and convert the same or else I have to throw some exception.
How can I resolve this?
You could call toInt() on your String instances:
fun main(args: Array<String>) {
for (str in args) {
try {
val parsedInt = str.toInt()
println("The parsed int is $parsedInt")
} catch (nfe: NumberFormatException) {
// not a valid int
}
}
}
Or toIntOrNull() as an alternative:
for (str in args) {
val parsedInt = str.toIntOrNull()
if (parsedInt != null) {
println("The parsed int is $parsedInt")
} else {
// not a valid int
}
}
If you don't care about the invalid values, then you could combine toIntOrNull() with the safe call operator and a scope function, for example:
for (str in args) {
str.toIntOrNull()?.let {
println("The parsed int is $it")
}
}
Actually, there are several ways:
Given:
// aString is the string that we want to convert to number
// defaultValue is the backup value (integer) we'll have in case of conversion failed
var aString: String = "aString"
var defaultValue : Int = defaultValue
Then we have:
Operation
Successful operation
Unsuccessful Operation
aString.toInt()
Numeric value
NumberFormatException
aString.toIntOrNull()
Numeric value
null
aString.toIntOrNull() ?: defaultValue
Numeric value
defaultValue
If aString is a valid integer, then we will get is numeric value, else, based on the function used, see a result in column Unsuccessful Operation.
val i = "42".toIntOrNull()
Keep in mind that the result is nullable as the name suggests.
As suggested above, use toIntOrNull().
Parses the string as an [Int] number and returns the result
or null if the string is not a valid representation of a number.
val a = "11".toIntOrNull() // 11
val b = "-11".toIntOrNull() // -11
val c = "11.7".toIntOrNull() // null
val d = "11.0".toIntOrNull() // null
val e = "abc".toIntOrNull() // null
val f = null?.toIntOrNull() // null
I use this util function:
fun safeInt(text: String, fallback: Int): Int {
return text.toIntOrNull() ?: fallback
}
In Kotlin:
Simply do that
val abc = try {stringNumber.toInt()}catch (e:Exception){0}
In catch block you can set default value for any case string is not converted to "Int".
string_name.toString().toInt()
converts string_name to String and then the resulting String is converted to int.
i would go with something like this.
import java.util.*
fun String?.asOptionalInt() = Optional.ofNullable(this).map { it.toIntOrNull() }
fun main(args: Array<String>) {
val intArgs = args.map {
it.asOptionalInt().orElseThrow {
IllegalArgumentException("cannot parse to int $it")
}
}
println(intArgs)
}
this is quite a nice way to do this, without introducing unsafe nullable values.
add (?) before fun toInt()
val number_int = str?.toInt()
You can Direct Change by using readLine()!!.toInt()
Example:
fun main(){
print("Enter the radius = ")
var r1 = readLine()!!.toInt()
var area = (3.14*r1*r1)
println("Area is $area")
}
fun getIntValueFromString(value : String): Int {
var returnValue = ""
value.forEach {
val item = it.toString().toIntOrNull()
if(item is Int){
returnValue += item.toString()
}
}
return returnValue.toInt()
}

How would I write this in idiomatic Kotlin?

I have some code:
private fun getTouchX(): Int {
arguments ?: return centerX()
return if (arguments.containsKey(KEY_DOWN_X)) {
arguments.getInt(KEY_DOWN_X)
} else {
centerX()
}
}
private fun centerX() = (views.rootView?.width ?: 0) / 2
and I want to shorten it.
in the function getTouchX, there are two return conditions duplicated. (which is centerX)
I tried to do this:
private fun getTouchX(): Int {
if (arguments == null || !arguments.containsKey(KEY_DOWN_X)) {
return centerX()
}
return arguments.getInt(KEY_DOWN_X)
}
However, it looks more like Java than Kotlin.
How could I go about writing this in idiomatic Kotlin?
I'm not sure where arguments is coming from, but a cleaner solution would be
private fun getTouchX(): Int =
if(arguments?.containsKey(KEY_DOWN_X) == true) {
arguments.getInt(KEY_DOWN_X)
} else {
centerX()
}
The if only calls containsKey if arguments is non-null, otherwise the left side of == resolves to null. null != true, so it will return centerX() from else.
Similarly if arguments is non-null, then the result of containsKey will be used to resolve.
And now that there's only one expression, can use body expression format.
I'd probably go with an expression function with a when expression:
private fun getTouchX() = when {
arguments == null || !arguments.containsKey(KEY_DOWN_X) -> centerX()
else -> arguments.getInt(KEY_DOWN_X)
}
You could also consider declaring touchX as a private val:
private val touchX: Int
get() = when {
arguments == null || !arguments.containsKey(KEY_DOWN_X) -> centerX()
else -> arguments.getInt(KEY_DOWN_X)
}
Looking at just the plain Kotlin code, my suggestion would be:
private fun getTouchX() =
arguments?.let {
if (!it.containsKey(KEY_DOWN_X))
return#let null
it.getInt(KEY_DOWN_X)
} ?: centerX()
But if arguments is a descendent of an Android BaseBundle, you might further compress this to:
private fun getTouchX() = arguments?.getInt(KEY_DOWN_X, centerX()) ?: centerX()
Note: As the method signature suspiciously looks like reading a property, you might consider turning it into a read-only property.

Swift 'if let' statement equivalent in Kotlin

In Kotlin is there an equivalent to the Swift code below?
if let a = b.val {
} else {
}
You can use the let-function like this:
val a = b?.let {
// If b is not null.
} ?: run {
// If b is null.
}
Note that you need to call the run function only if you need a block of code. You can remove the run-block if you only have a oneliner after the elvis-operator (?:).
Be aware that the run block will be evaluated either if b is null, or if the let-block evaluates to null.
Because of this, you usually want just an if expression.
val a = if (b == null) {
// ...
} else {
// ...
}
In this case, the else-block will only be evaluated if b is not null.
Let's first ensure we understand the semantics of the provided Swift idiom:
if let a = <expr> {
// then-block
}
else {
// else-block
}
It means this: "if the <expr> results in a non-nil optional, enter the then-block with the symbol a bound to the unwrapped value. Otherwise enter the else block.
Especially note that a is bound only within the then-block. In Kotlin you can easily get this by calling
<expr>?.also { a ->
// then-block
}
and you can add an else-block like this:
<expr>?.also { a ->
// then-block
} ?: run {
// else-block
}
This results in the same semantics as the Swift idiom.
My answer is totally a copy cat from the others. However, I cannot understand their expression easily. So I guess it would be nice to provide an more understandable answer.
In swift:
if let a = b.val {
//use "a" as unwrapped
}
else {
}
In Kotlin:
b.val?.let{a ->
//use "a" as unwrapped
} ?: run{
//else case
}
if let statement
Swift if let Optional Binding (so called if-let statement) is used to extract a non-optional value if one exists, or to do nothing if a value is nil.
Swift's if-let statement:
let b: Int? = 50
if let a: Int = b {
print("Good news!")
} else {
print("Equal to 'nil' or not set")
}
/* RESULT: Good news! */
In Kotlin, like in Swift, to avoid crashes caused by trying to access a null value when it’s not expected, a specific syntax (like b.let { } in second example) is provided for properly unwrapping nullable types:
Kotlin's equivalent1 of Swift's if-let statement:
val b: Int? = null
val a = b
if (a != null) {
println("Good news!")
} else {
println("Equal to 'null' or not set")
}
/* RESULT: Equal to 'null' or not set */
Kotlin’s let method, when used in combination with the safe-call operator ?:, provides a concise way to handle nullable expressions.
Kotlin's inline let function and Elvis Operator of Swift's nil coalescing operator:
val b: Int? = null
val a = b.let { nonNullable -> nonNullable } ?: "Equal to 'null' or not set"
println(a)
/* RESULT: Equal to 'null' or not set */
guard let statement
guard-let statement in Swift is simple and powerful. It checks for some condition and if it evaluates to be false, then the else statement executes which normally will exit a method.
Let's explore a Swift's guard-let statement:
let b: Int? = nil
func method() {
guard let a: Int = b else {
print("Equal to 'nil' or not set")
return
}
print("Good news!")
}
method()
/* RESULT: Equal to 'nil' or not set */
Kotlin's similar effect of Swift's guard-let statement:
Unlike Swift, in Kotlin, there is no guard statement at all. However, you can use the Elvis Operator – ?: for getting a similar effect.
val b: Int? = 50
fun method() {
val a = b ?: return println("Equal to 'null' or not set")
return println("Good news!")
}
method()
/* RESULT: Good news! */
there are two answers above, both got a lot acceptances:
str?.let{ } ?: run { }
str?.also{ } ?: run { }
Both seem to work in most of the usages, but #1 would fail in the following test:
#2 seems better.
Unlike Swift, Its not necessary to unwrap the optional before using it in Kotlin. We could just check if the value is non null and the compiler tracks the information about the check you performed and allows to use it as unwrapped.
In Swift:
if let a = b.val {
//use "a" as unwrapped
} else {
}
In Kotlin:
if b.val != null {
//use "b.val" as unwrapped
} else {
}
Refer Documentation: (null-safety) for more such use cases
Here's how to only execute code when name is not null:
var name: String? = null
name?.let { nameUnwrapp ->
println(nameUnwrapp) // not printed because name was null
}
name = "Alex"
name?.let { nameUnwrapp ->
println(nameUnwrapp) // printed "Alex"
}
Here's my variant, limited to the very common "if not null" case.
First of all, define this somewhere:
inline fun <T> ifNotNull(obj: T?, block: (T) -> Unit) {
if (obj != null) {
block(obj)
}
}
It should probably be internal, to avoid conflicts.
Now, convert this Swift code:
if let item = obj.item {
doSomething(item)
}
To this Kotlin code:
ifNotNull(obj.item) { item ->
doSomething(item)
}
Note that as always with blocks in Kotlin, you can drop the argument and use it:
ifNotNull(obj.item) {
doSomething(it)
}
But if the block is more than 1-2 lines, it's probably best to be explicit.
This is as similar to Swift as I could find.
There is a similar way in kotlin to achieve Swift's style if-let
if (val a = b) {
a.doFirst()
a.doSecond()
}
You can also assigned multiple nullable values
if (val name = nullableName, val age = nullableAge) {
doSomething(name, age)
}
This kind of approach will be more suitable if the nullable values is used for more than 1 times. In my opinion, it helps from the performance aspect because the nullable value will be checked only once.
source: Kotlin Discussion
I'm adding this answer to clarify the accepted answer because it's too big for a comment.
The general pattern here is that you can use any combination of the Scope Functions available in Kotlin separated by the Elvis Operator like this:
<nullable>?.<scope function> {
// code if not null
} :? <scope function> {
// code if null
}
For example:
val gradedStudent = student?.apply {
grade = newGrade
} :? with(newGrade) {
Student().apply { grade = newGrade }
}
The cleanest option in my opinion is this
Swift:
if let a = b.val {
} else {
}
Kotlin
b.val.also { a ->
} ?: run {
}
Swift if let statement in Kotlin
The short answer is use simple IF-ELSE as by the time of this comment there is no equivalent in Kotlin LET,
if(A.isNull()){
// A is null
}else{
// A is not null
}
we can get the same Unwraping syntax like Swift if let using inline fun
inline fun <T:Any?> T?.unwrap(callback: (T)-> Unit) : Boolean {
return if (this != null) {
this?.let(callback)
true
}else {
false
}
}
Uses: :
val name : String? = null
val rollNo : String? = ""
var namesList: ArrayList<String>? = null
if (name.unwrap { name ->
Log.i("Dhiru", "Name have value on it $name")
})else if ( rollNo.unwrap {
Log.i("Dhiru","Roll have value on it")
}) else if (namesList.unwrap { namesList ->
Log.i("Dhiru","This is Called when names list have value ")
}) {
Log.i("Dhiru","No Field have value on it ")
}
The problem with the Any?.let {} ?: run {} constructions is that:
It only allows for one non-null check per statement
If the let block returns null the run block is evaluated anyway
It's not possible to perform multiple checks in a switch/when style
A solution which tackles most of these problems is to define functions like the following:
private inline fun <A> ifNotNull(p1: A?, block: (A) -> Unit): Unit? {
if (p1 != null) {
return block.invoke(p1)
}
return null
}
private inline fun <A, B> ifNotNull(p1: A?, p2: B?, block: (A, B) -> Unit): Unit? {
if (p1 != null && p2 != null) {
return block.invoke(p1, p2)
}
return null
}
private inline fun <A, B, C> ifNotNull(p1: A?, p2: B?, p3: C?, block: (A, B, C) -> Unit): Unit? {
if (p1 != null && p2 != null && p3 != null) {
return block.invoke(p1, p2, p3)
}
return null
}
This would allow for a statement like:
ifNotNull(a, b) { a, b ->
// code when a, b are not null
} ?:
ifNotNull(c) { c ->
// code when a, b are null and c not null
} ?:
ifNotNull(d, e, f) { d, e, f ->
// code when a, b, c are null and d, e, f not null
} ?: run {
// code which should be performed if a, b, c, d, e and f are null
}
The only caveat is that continue and break statements are not supported if executed within a loop compared to Swift's if let equivalent.
Probably I am very late however the easiest way to unwrap and option is
yourOptionalString ?: return
this was all the following lines will have unwrapped string
If b is a member variable then this approach seems most readable to me:
val b = this.b
if (b == null) {
return
}
println("non nullable : ${b}")
This is also consistent with how it works in swift, where a new local variable shadows the member variable.