I am tinkering around with Kotlin and I am trying to wrap my head around how nullable variables work in Kotlin. Here I have a piece of code that does a boolean check to see if a vehicle is over capacity. Is the implementation a good way to work with nullable variables or is there a more elegant way ?
class Route(var vehicle: Vehicle?, var jobs: List<Job>?) {
constructor()
constructor(vehicle: Vehicle?)
fun isOverCapacity() : Boolean {
val vehicleCapacity = vehicle?.capacity
if (vehicleCapacity != null){
val totalDemand = jobs?.sumBy { job -> job.demand }
if (totalDemand != null) {
return totalDemand > vehicleCapacity
}
}
return false
}
}
Thanks a lot!
fun isOverCapacity(): Boolean {
val vehicleCapacity = vehicle?.capacity ?: return false
val totalDemand = jobs?.sumBy { job -> job.demand } ?: return false
return totalDemand > vehicleCapacity
}
What does ?: do in Kotlin? (Elvis Operator)
By using kotlin std-lib dsl functional operators like let, run, also, apply, use.
Use of ?. -> if the object/value is not null then only call the next function.
let -> returns the result of lambda expression.
run -> returns the result of lambda expression passing this as receiver.
also -> does operation and returns itself unlike the result of lambda.
apply -> does operation and returns itself unlike the result of lambda passing this as receiver.
use -> returns the result of lambda expression and closes the Closeable resource.
You can simplify the code as follows:
fun isOverCapacity() : Boolean =
vehicle?.capacity?.let { vehicleCapacity ->
jobs?.sumBy { job -> job.demand }?.let { totalDemand ->
totalDemand > vehicleCapacity
}
} ?: false
Related
val customerInfo = when {
visitor.isCustomer -> customerService.getCustomerInfo(visitorId )
else -> null
}
In this Code, visitor.isCustomer is Boolean ( true / false)
Now then, I don't like specify else -> null into the code.
so i want to delete when statement and convert other ways..
How can I do that?
(I prefer to convert it with StandardKt (like let, apply, also... ))
You can just use an if/else
val customerInfo = if (visitor.isCustomer) customerService.getCustomerInfo(visitorId) else null
You could do something like
val customerInfo = vistorId.takeIf { visitor.isCustomer }?.let { customerService.getCustomerInfo(it) }
But I think a when or if statement is cleaner and more readable.
I think JetBrains coding convention would recommend an if statement instead of a when statement here.
Hope this will be more readable.
Without any additional things,
val customerInfo = if (visitor.isCustomer) customerService.getCustomerInfo(visitorId) else null
With your own extension functions
2)Without infix: (condition).ifTrueElseNull{ return value}
inline fun <T> Boolean?.ifTrueElseNull(block: () -> T): T? {
if (this == true) {
return block()
}
return null
}
var a = visitor.isCustomer.ifTrueElseNull{customerService.getCustomerInfo(visitorId)}
With infix: (condition) ifTrueElseNull{ return value}
inline infix fun <T> Boolean?.ifTrueElseNull(block: () -> T): T? {
if (this == true) {
return block()
}
return null
}
var a = visitor.isCustomer ifTrueElseNull{customerService.getCustomerInfo(visitorId)}
Consider this nice utility extension function i wanted to use :
inline infix fun <T> T?.otherwise(other: () -> Unit): T? {
if (this != null) return this
other()
return null
}
It could be very useful for logging stuff when expressions evaluated to null for example:
val x: Any? = null
x?.let { doSomeStuff() } otherwise {Log.d(TAG,"Otherwise happened")}
but I see that it wont work for :
val x: Any? = null
x?.otherwise {Log.d(TAG,"Otherwise happened")}
see here for running example
Well when thinking about it i guess that makes sense that if x is null the ? makes the postfix not be executed, but i dont understand why the let in the first example is any different?
Is it possible to fix the utility to be more robust and work without having to have let in the chain?
First, you can simplify the implementation:
inline infix fun <T> T?.otherwise(other: () -> Unit): T? {
if (this == null) { other() }
return this
}
Or
inline infix fun <T> T?.otherwise(other: () -> Unit): T? =
also { if (it == null) other() }
When you do this:
null?.otherwise { println("Otherwise happened") }
?. means "execute if not null", so otherwise is not executed.
What you need to write is:
null otherwise { println("Otherwise happened") }
Note this is very similar to the ?: operator (as Vadik pointed out in the comments):
null ?: println("Otherwise happened")
The difference is that otherwise always returns the value on the left (the same as also), but ?: returns the value on the right when the value on the left is null.
In my opinion, otherwise is confusing, especially as it always returns the left value despite the name. You would be better to use the ?: operator. Or perhaps rename it to something like alsoIfNull.
The let example executes because, when you don't utilize the infix feature, it looks like this:
x?.let {}.otherwise {println("1")}
Notice that it's not ?.otherwise; therefore, it always executes.
So to use otherwise without let, you can omit the ?.
x.otherwise { ... }
x?.let { doSomeStuff() }.otherwise {Log.d(TAG,"Otherwise happened")}
// ⬇️
val value = if (x != null) {
doSomeStuff()
} else {
null
}
value.otherwise {Log.d(TAG,"Otherwise happened")}
x?.otherwise { Log.d(TAG,"Otherwise happened") }
// ⬇️
if (x != null) {
otherwise { Log.d(TAG,"Otherwise happened") }
} else {
null
}
?. means if the value is not null then execute the method and return the result otherwise return null
I have the following functions to simulate the ternary operator for kotlin
fun Boolean.then(action: () -> Unit): Boolean {
if (this)
action.invoke()
return this
}
fun Boolean.otherwise(action: () -> Unit) {
if (!this)
action.invoke()
}
fun <T> Boolean.then(func: () -> T): T? {
if (this)
return func.invoke()
return null
}
fun <T> T?.otherwise(action: () -> T): T {
return this ?: action.invoke()
}
they are supposed to be used like this :
(check).then { doHello() }.otherwise { doWorld() }
val answer = (check).then { "hello" }.otherwise { "world" }
however when I try to assign a value using the above operators like this:
val visibility: Int = (show).then { View.VISIBLE }.alt { View.GONE }
I get an error saying that the required reply was Int but it actually got Unit which means that it called the first version of the methods instead of the second
Other than renaming the methods (when I changed the first two to thenDo and otherwiseDo it worked), can I write the above code in some way so that the compiler will know to call the second version?
I don't think you need both overloads. If you remove the ones that return Unit, then both your lines of code work:
(check).then { doHello() }.otherwise { doWorld() }
val answer = (check).then { "hello" }.otherwise { "world" }
That's because the first line, where the lambdas return Unit, e.g. doHello(), can still use the generic versions of then and otherwise, as they are still considered functions with a return value, namely Unit.
Although I agree with some the comments above: do you really need this? Why not just use if, which is an expression which returns a value (like the ternary operator). See discussion here for more info.
i want to create a closure where it take all the Boolean expression and methods, gives the final result
something like this
myAnd{
23<34
false
someFunction() //returns true
}
so the answer will be false
The solution i came with is this
fun myAnd(vararg flags: Boolean) = flags.all { it }
myAnd(true , false ,someFunction())
but this solution won't give the power of short circuiting while using and operator
Using blocks to implement short-circuiting and
fun someFunction() = true
fun and(vararg parameters: ()->Boolean): Boolean {
return parameters.all { it() }
}
fun main(args: Array<String>) {
and (
{23<34},
{false},
::someFunction
)
}
This works by passing in each predicate as a block, which means that they can be evaluated one by one, and if any of them returns false then all will return false immediately, short-circuiting the rest of them.
I can think of a quick solution that enables the following use:
val boolResult: Boolean = myAnd {
+ (23 < 34)
+ false
+ someFunction() //returns true
}
Here's the relevant code:
fun myAnd(bb: BooleanBuilder.() -> Unit) = BooleanBuilder().apply { bb() }.result
class BooleanBuilder {
var bools = listOf<Boolean>()
operator fun Boolean.unaryPlus() {
bools += this
}
fun result() = bools.all { it }
}
Explanation: It's a lightweight example of a custom DSL. The interesting part of this is BooleanBuilder.() -> Unit, a function literal with receiver, so that I can pass a lambda that's in the scope of a BooleanBuilder (lambda's receiver). This is necessary to use the member extension Boolean.unaryPlus that again enables the use of + boolean as shown.
Should I use double =, or triple =?
if(a === null) {
//do something
}
or
if(a == null) {
//do something
}
Similarly for 'not equals':
if(a !== null) {
//do something
}
or
if(a != null) {
//do something
}
A structural equality a == b is translated to
a?.equals(b) ?: (b === null)
Therefore when comparing to null, the structural equality a == null is translated to a referential equality a === null.
According to the docs, there is no point in optimizing your code, so you can use a == null and a != null
Note that if the variable is a mutable property, you won't be able to smart cast it to its non-nullable type inside the if statement (because the value might have been modified by another thread) and you'd have to use the safe call operator with let instead.
Safe call operator ?.
a?.let {
// not null do something
println(it)
println("not null")
}
You can use it in combination with the Elvis operator.
Elvis operator ?: (I'm guessing because the interrogation mark looks like Elvis' hair)
a ?: println("null")
And if you want to run a block of code
a ?: run {
println("null")
println("The King has left the building")
}
Combining the two
a?.let {
println("not null")
println("Wop-bop-a-loom-a-boom-bam-boom")
} ?: run {
println("null")
println("When things go null, don't go with them")
}
Kotlin ways of handling null
Secure Access Operation
val dialog : Dialog? = Dialog()
dialog?.dismiss() // if the dialog will be null,the dismiss call will be omitted
Let function
user?.let {
//Work with non-null user
handleNonNullUser(user)
}
Early exit
fun handleUser(user : User?) {
user ?: return //exit the function if user is null
//Now the compiler knows user is non-null
}
Immutable shadows
var user : User? = null
fun handleUser() {
val user = user ?: return //Return if null, otherwise create immutable shadow
//Work with a local, non-null variable named user
}
Default value
fun getUserName(): String {
//If our nullable reference is not null, use it, otherwise use non-null value
return userName ?: "Anonymous"
}
Use val instead of var
val is read-only, var is mutable. It’s recommended to use as many read-only properties as you can, they are thread-safe.
Use lateinit
Sometimes you can’t use immutable properties. For example, it happens on Android when some property is initialized in onCreate() call. For these situations, Kotlin has a language feature called lateinit.
private lateinit var mAdapter: RecyclerAdapter<Transaction>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAdapter = RecyclerAdapter(R.layout.item_transaction)
}
fun updateTransactions() {
mAdapter.notifyDataSetChanged()
}
Both approaches generate the same bytecode so you can choose whatever you prefer.
Addition to #Benito Bertoli,
the combination is actually unlike if-else
"test" ?. let {
println ( "1. it=$it" )
} ?: let {
println ( "2. it is null!" )
}
The result is:
1. it=test
But if:
"test" ?. let {
println ( "1. it=$it" )
null // finally returns null
} ?: let {
println ( "2. it is null!" )
}
The result is:
1. it=test
2. it is null!
Also, if use elvis first:
null ?: let {
println ( "1. it is null!" )
} ?. let {
println ( "2. it=$it" )
}
The result is:
1. it is null!
2. it=kotlin.Unit
Check useful methods out, it could be useful:
/**
* Performs [R] when [T] is not null. Block [R] will have context of [T]
*/
inline fun <T : Any, R> ifNotNull(input: T?, callback: (T) -> R): R? {
return input?.let(callback)
}
/**
* Checking if [T] is not `null` and if its function completes or satisfies to some condition.
*/
inline fun <T: Any> T?.isNotNullAndSatisfies(check: T.() -> Boolean?): Boolean{
return ifNotNull(this) { it.run(check) } ?: false
}
Below is possible example how to use those functions:
var s: String? = null
// ...
if (s.isNotNullAndSatisfies{ isEmpty() }{
// do something
}
I want to respond to answers of #Benito Bertoli and #BingLi224 and provide imho correct solution.
Problem is with using let, because result of let is it's last expression. You just want to pass the same thing as is passed into it, so also is a better solution. At the same time, after using elvis operator, let is impossible to use, because there is no object to call extension function to so I am using run (functional version). More on that in the scope functions official documentation
Another downside of this compared to using if/when is not being able to use this as an expression so I wouldn't recommend using it :-)
Final code:
"test"?.also {
println("1. it=$it")
} ?: run {
println("2. it is null!")
}
"test"?.also {
println("1. it=$it")
null
} ?: run {
println("2. it is null!")
}
null?.also {
println("1. it is null!")
} ?: run {
println("2. it is null")
}
null?.also {
println("1. it is null!")
null
} ?: run {
println("2. it is null")
}
And output:
1. it=test
1. it=test
2. it is null
2. it is null