New at Kotlin here and trying to learn the best way to use the higher order functions and passing lambdas. I've created this method to call an API and return an object created from a string OR return a failure if something went wrong.
fun getDeviceStatus(onSuccess: (Device) -> Unit, onFailure: ((String) -> Unit)? = null) {
FuelClient.get(DEVICE_URL,
success = { responseString ->
val adapter = MoshiUtil.moshi.adapter(Device::class.java)
val deivce= adapter.fromJson(responseString)!!
onSuccess(device)
},
failure = { onFailure?.invoke(it.message!!)})
}
I can use this function fine like so:
DeviceService.getDeviceStatus(
{ w ->
print("device")
},
{ e -> print(e) })
But it bothers me a bit that I can't see the name of the functions to see what each function does. I"m wondering if there is a cleaner/better way to do this, like
DeviceService.getDeviceStatus(){
onSuccess{print("device")}
onFailure{print("error")}
}
or maybe
DeviceService.getDeviceStatus()
.onSuccess{print("device")}
.onFailure{print("error")}
But those gives errors. Any thoughts on how to best handle the onSuccess/onFailure use case that is very common? Thx
Lambda expression syntaxParameter declarations in the full syntactic form go inside curly braces and have optional type annotations. The body goes after the -> . If the inferred return type of the lambda is not Unit , the last (or possibly single) expression inside the lambda body is treated as the return value.
A lambda expression is a shorter way of describing a function. It doesn't need a name or a return statement. You can store lambda expressions in a variable and execute them as regular functions. They can also be passed as parameters to other functions or be the return value.
To help deal with this, Kotlin supports a specific kind of syntax referred to as trailing lambda syntax. This syntax states that if the final parameter to a function is another function, then the lambda can be passed outside of the function call parentheses.
Lambda functions are inline functions and thus execute comparatively faster.
You can attach a name to each variable in kotlin. Change your code like this
DeviceService.getDeviceStatus(
onSuccess = { w ->
print("device")
},
onFailure = { e -> print(e) })
For this specific case, when the second lambda is optional, infix
functions work very well:
sealed class DeviceStatusResult {
abstract infix fun onFailure(handler: (String) -> Unit)
}
class DeviceStatusSuccess(val device: Device) : DeviceStatusResult() {
override fun onFailure(handler: (String) -> Unit) = Unit
}
class DeviceStatusFailure(val errorMessage: String) : DeviceStatusResult() {
override fun onFailure(handler: (String) -> Unit) = handler(errorMessage)
}
fun getDeviceStatus(onSuccess: (Device) -> Unit): DeviceStatusResult {
// get device status
// if (success)
val device = Device()
onSuccess(device)
return DeviceStatusSuccess(device)
// else
// return DeviceStatusFailure(message)
}
Then it can used like
getDeviceStatus { device ->
println(device)
} onFailure { errorMessage ->
System.err.println(errorMessage)
}
Maybe onFailure
should be called orFail
or something like that.
It is good when the second argument is optional, but not so much otherwise because it doesn't force the user to actually supply a failure handler. And I don't think it's a good idea because it will be too easy to accidentally omit a failure handler. It's much better to force the user to provide one, even if it happens to be an empty one. Therefore, it is better to use named arguments for this case, even though nothing forces to actually name them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With