I am wondering if there is a better way of null checking and assigning value like in this example
val result: Clazz? = if (variable == null) null else someFun(variable)
variable?.let(::someFun)
You can use the Kotlin scope function let .
let is often used for executing a code block only with non-null values. To perform actions on a non-null object, use the safe call operator ?. on it and call let with the actions in its lambda.
So in this case someFun is only called if variable is not null. Otherwise null is returned.
The longer form of this would be:
variable?.let { someFun(it) }
where it is the non-null value of variable. However:
If the code block contains a single function with it as an argument, you can use the method reference (::) instead of the lambda:
So we can shorten it to the
variable?.let(::someFun)
form
Related
I have a function for validating the request, it can't be null or empty, I can test if the end point is empty, but I can't pass the value as null to the request in test, I will get "NulllPointerException", how can I test null case?
fun validateRequest(request: RegisteRequest) {
validateEndPoint(request.EendPoint)
}
private fun validateEndPoint(endPoint: String) {
if (endPoint.isNullOrEmpty()) {
logger.error("Request is missing EndPoint")
throw IllegalArgumentException(ERROR_MESSAGE_MISSING_END_POINT)
}
}
Kotlin's type system is able to differentiate between values that can be null (e.g., String?) and values that can't be null (e.g., String). You can find more details here.
In your case you're defining a function that takes in input a non-null String, so if you're invoking it from Kotlin, the Kotlin compiler will make sure you don't pass a null value – it'll fail the build if you try to do so.
If somehow you still manage to pass a null value (e.g., via reflection or by invoking the function from Java), you'll get a NullPointerException (as documented here), because the Kotlin compiler will actually insert some instructions under the hood to make sure you don't provide a null value.
So if your code is meant to be called from Java or if you really want to test what happens if you pass null, you'll have to assert that a NullPointerException is thrown.
If, instead, null is a valid value and you want to handle it differently, you'll have to change the signature of your method so that it accepts a null parameter (note the type of the parameter here is String?):
private fun validateEndPoint(endPoint: String?) {
...
}
Just learned Kotlin Nullable type and let{} function which replaces the if (xx != null) {} operation.
But one thing I am confused is that, we all know and I Think the Complier Should Know that when we use let{}, the variable/object who is calling this function is possiblly null, however the complier still requires me to add the safe call operator "?" after the variable name instead of providing Smart Cast like it does in if (xx != null) {}. Why?
My piece of code:
fun main() {
var number1: Int? = null
//val number2 = number1.let { it + 1 } ?: 10 //doesn't work, not quite "smart"
val number2 = number1?.let { it + 1 } ?: 10 //works, must have "?"
println(number1)
println(number2)
}
You've already got answers in the comments, but just to explain the ? thing...
Kotlin lets you make null-safe calls on nullable variables and properties, by adding ? before the call. You can chain this too, by doing
nullableObject?.someProperty?.someFunction()
which evaluates nullableObject, and if it's non-null it evaluates the next bit, otherwise the whole expression evaluates to null. If any part of the chain evaluates as null, the whole expression returns null.
So it has this short-circuiting effect, and you can use the elvis "if null" operator to create a default value if you can't evaluate the whole chain to a non-null result:
nullableObject?.nullableProperty?.someFunction() ?: defaultAction()
and once you introduce the null check in the chain, you have to add it for every call after that - it's basically propagating either the result of the previous bit, or the null it resolved to, so there's a null check at each step
The let block is just a scope function - you use it on a value, so you can run some code either using that value as a parameter or a receiver (a variable or this basically). It also has the side effect of creating a new temporary local variable holding that value, so if the original is a var it doesn't matter if that value changes, because your let code isn't referring to that variable anymore.
So it's useful for doing null checks one time, without worrying the underlying value could become null while you're doing stuff with it:
nullableVar?.let { it.definitelyIsNotNull() }
and the compiler will recognise that and smart cast it to a non-null type. An if (nullableVar != null) check can't guarantee that nullableVar won't be null by the time the next line is executed.
I am trying to make use of
infix fun <T> Boolean.then(param: T): T? = if (this) param else null
but it throws ArrayIndexOutOfBoundsException for
(index > 0) then data[index - 1].id, where index == 0
since
data[-1] doesn't exist.
How could I make it work in Kotlin?
In Kotlin, function parameters are evaluated eagerly: when you call a function, the value of each parameter is worked out before passing control to the function. This happens regardless of whether the value will get used within the function. (After all, in general you can't tell whether it will be used without actually running the code.*)
(This is true for infix functions as well as standard calls; the meaning is exactly the same, despite the different-looking syntax.)
In fact, this is true for most other operators, too: when you add two numbers, concatenate two strings, return a value from a function, or whatever, each operand is evaluated first. There are only a few exceptions, of which the short-circuiting && and || operators are the most obvious.
So in your case, a call such as then data[index - 1].id will always first evaluate data[index - 1].id before passing it to the then() function; so if index is 0, that will throw an ArrayIndexOutOfBoundsException, as you see.
If you don't want it evaluated, then you have to pass a lambda instead, e.g.:
infix fun <T> Boolean.then(lazyValue: () -> T): T?
= if (this) lazyValue() else null
Then you can use it like this:
(index > 0) then { data[index - 1].id }
What happens there is that the code is evaluated, giving a lambda, which is passed to the function as lazyValue; but the content of the lambda isn't evaluated unless/until* it gets to lazyValue() in the function.
You can see this pattern in library functions such as require(value, lazyMessage); since the requirement will nearly always be satisfied, and the message is usually a complex string that has to be constructed at runtime, this avoids creating unnecessary String objects by evaluating its second argument only if the condition is false.
The down-side of passing a lambda is that it's marginally less efficient: it needs to create an object to represent the lambda, so that adds a little extra CPU and heap. (Depending on the circumstances, it may need to create a new object each time, or it may be able to reuse the same one.)
But Kotlin provides a way around that: if you mark the function as inline, then it avoids both the function call and the lambda, and effectively ‘pastes’ your code directly into the inlined copy of the function, making it just as efficient as if you'd written out the function body ‘by hand’.
(* There's an experimental feature called contracts which will let you tell the compiler if you do know for sure under what circumstances a lambda will be evaluated. That could potentially avoid some types of warning or error — though it doesn't change the evaluation order, so you'd still need a lambda here.)
Question is simple:
In Kotlin, when I instantiate a fragments arguments with a Bundle(), the system still needs the arguments object to be reassured with !!. The arguments should be definitely not null by now, right? So why is that needed?
Here is the code:
private fun openPinCodeFragment(mode: PinView.Mode) {
currentFragment = PinCodeFragment()
currentFragment?.run {
arguments = Bundle()
arguments!!.putSerializable(MODE, mode)
}
openFragment(currentFragment)
}
If I remove the !! then:
You're setting the value of a variable which was defined outside of this scope (the declaration of arguments is not visible in your code).
No matter what you assign, it could have been changed by the time code execution reaches the next line to a null value by another Thread, that's why you have to use the !! here. I'd suggest defining arguments either in local scope with val or making it non-nullable in its definition.
This happens because arguments is of Bundle? type, this means that it can be either Bundle or null.
Instead of using an if to check whether it is null, like you would to in Java, the operators !! and ? were introduced.
For example if you want your code to be correct without using !! you could add:
if (arguments != null) {
arguments.putSerializable(MODE, mode)
}
Using these operators you have the following flexibility:
!! - you tell the compiler that the value cannot be null, it will throw error otherwise;
? - you don't care that much, if it is not null then it will access method and might return result, otherwise the result of such call is null and no action is made.
I'd like to know how to call a function with default arguments when you want to specify the value of the second argument. In the simple example below I am showing that the addTwo() takes two arguments. The 'first' argument has a default value but the 'second does not. How would I call this function specifying that I want to use the default value for 'first' with the given value of 2 for 'second'?
Calling addTwo(2) throws an error.
fun main(args: Array<String>) {
var sum = addTwo(1,2) // works fine
var nextSum = addTwo(2) // ERROR: No value passed for parameter second
}
fun addTwo(first: Int = 0, second: Int): Int {
return first + second
}
This is an error because Kotlin does not know why you omitted a second parameter. The first can be defaulted, but not second. Therefore when you only passed in one argument it was for parameter first. The error message says exactly what the problem is:
no value passed for parameter second
You must call using named arguments for anything after the first defaulted parameter that you want to leave blank. For your case, this would be:
addTwo(second = 2) // first defaulted to 0
Had your defaulted parameters been in the other order, your call would be fine.
fun addTwo(first: Int, second: Int = 0): Int{
return first + second
}
addTow(2) // no error, second defaulted to 0
Had your data types been different for the two parameters, you would have seen a different less clear error letting you know the type of the first parameter (Int) was expected, not the type of the second (Date in the following example):
fun something(first: Int = 0, second: Date): Date { ... }
something(Date()) // ERROR: Type mismatch: inferred type is java.util.Date but kotlin.Int was expected
Note: You can run into the same issue with vararg:
Only one parameter may be marked as vararg. If a vararg parameter is not the last one in the list, values for the following parameters can be passed using the named argument syntax
Additional note: because of what you experienced, it is generally a best practice to make default arguments the last parameters in the list. Some languages even force this, such as Python (except that Python allows forced named arguments after defaults, since forcong you to call them by name is how you'd get around having parameters after default arguments anyway).