Reasonable situations to use Kotlin's `let` - android

In Kotlin, it's common to use let to execute code if an object (let receiver) isn't null, as an alternative to an if != null check, as in the following:
val nullable: String? = "anything"
nullable?.let {
println(it)
}
In what other situations does it make sense to make use of let?
FYI, let is part of Kotlin's stdlib and is defined as follows:
#kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)

I've seen let used to scope nested variables (ignoring let's return):
some.nested.foo.bar.let { bar ->
doSomething(bar)
doMoreStuff(bar)
}
It can be nice since it replaces the need to define a local variable, but I'm not sure how useful it actually is.
Could also be done with apply, though the readability is a bit worse, (this instead of bar in the scope).

let is also useful when you work with var variables which are nullable.
For instance, we have this code
fun doSomething(value: String) {
}
class A {
var boo: String? = null
fun b() {
if (boo != null) {
doSomething(boo)
}
}
}
There, we have compile-time error inside if block because boo can be changed outside. To fix that we should either create a val variable
class A {
var boo: String? = null
fun b() {
val b = boo
if (b != null) {
doSomething(b)
}
}
}
or use let
class A {
var boo: String? = null
fun b() {
boo?.let {
doSomething(it)
}
}
}

scoping capabilities
The stdlib function let enables you to confine a variable's scope to a specific block of code. Shown here:
compile("a:b:c")
"io.vertx:vertx".let { v->
compile("$v-lang-kotlin:$vertxVersion")
compile("$v-lang-kotlin-coroutines:$vertxVersion")
}
compile("x:y:z")
We could alternatively define a val v: String = "io.vertx:vertx" which might make the code more readable since that's what we'd do in Java for example. Yet, that val would be accessible throughout the whole method. We just want to have it available for the compile units with vertx involved, thus we confine the variable holding io.vertx:vertx to the let block.
Informations on the different scoping function alternatives can be found in this thread.

you can use let in let in let by naming
someMethodCall()?.let{ nameOne ->
// ....
// some code here
// ....
val resultCall = nameOne
someMethod2Call()?.let { -> nameTwo
// ...
val myVariable = nameTwo + resultCall
// ...
}
}

Related

The best solution for the main thread to obtain data in the coroutine to avoid null values?

I have the following code, in the click event I need to use the class that is queried from the database.
data class Work(...)
fun Compose(){
var work1: Work? = null
var work2: Work? = null
var work3: Work? = null
LaunchedEffect(true){
CoroutineScope(IO).launch {
work1 = workViewModel.getById(1)
work2 = workViewModel.getById(2)
work3 = workViewModel.getById(3)
}
}
Card(
modifier = Modifier
.clickable(onClick = {
val url = "https://www.google.com/"
when{
url.contains(work1?.baseUrl) -> {...}
url.contains(work2?.baseUrl) -> {...}
url.contains(work3?.baseUrl) -> {...}
}
})
){}
}
this creates a problem, work3?.baseUrl found String? type Required CharSequence type.
So far it seems that only the !! operator can successfully run this code. But this code is based on a database query, using the !! operator is very risky.
And if you add a null operator before this, also not working.
requireNotNull(work1)
when{
url.contains(work1.baseUrl) -> {...}
}
Smart cast to 'Work' is impossible, because 'work1' is a local variable that is captured by a changing closure
Can you tell me what is the best solution?
I suggest not having that logic in the Composable. Try to move that to a function in the ViewModel, something like:
private val url = "https://www.google.com/"
fun validateBaseUrl() {
viewModelScope.launch(Dispatchers.IO) {
workViewModel.getById(1)?.let {
if (url.contains(it)) { .. }
}
work2 = workViewModel.getById(2)?.let {
if (url.contains(it)) { .. }
}
work3 = workViewModel.getById(3)?.let {
if (url.contains(it)) { .. }
}
}
}
And then in the Composable would be something like:
fun Compose() {
Card(
modifier = Modifier
.clickable(onClick = { viewModel.validateBaseUrl() })
){}
}
Remember to use the State Hoisting instead of sending the view model through the Composables.
Finally, you would need to send back a State to the Composable, either using a LiveData or a StateFlow.
You need to create a scope for the work properties :
work1?.run { url.contains(baseUrl) } == true -> {...}
Within the run lambda the accessed object is immutable even if the work properties themselves are mutable.
The == true is needed because the left side of the comparison operator can be either a Boolean or null.
You could also define an extension function like this:
fun Work?.isContainedIn(url: String) = this?.run { url.contains(baseUrl) } == true
and then just do:
work1.isContainedIn(url) -> { }
work2.isContainedIn(url) -> { }
work3.isContainedIn(url) -> { }

What are the differences between kotlin variable functions and common fun functions?

There is a fun method0:
private fun method0() {
println("method0 fun")
}
And a var method0 :
var method0 = {
println("method0")
}
It seems they are used the same:
method0()
I found that both occur at the same time, and the fun function has a higher priority when the code calls.
Other than that, is there any difference between them?
The var way of doing it results in a functional object. The lambda content is wrapped as a functional object so it can be passed around like any other instance of a class. It can directly be used as a function parameter, for instance.
var method0 = {
println("method0")
}
fun doSomethingTwice(action: ()->Unit) {
repeat(2) { action() }
}
fun main() {
doSomethingTwice(method0)
}
And since it's marked as a var you can swap it out for a different function:
fun main() {
method0 = { println("hello, world!") }
doSomethingTwice(method0)
}
Note that this way of specifying a function is a little bit heavier since it is wrapping the function in another class instance.
And you can still wrap any "regular" function into a functional object at any time by using :: to avoid doing it until it's necessary.
fun method0() {
println("method0")
}
fun main() {
doSomethingTwice(::method0)
}

Android kotlin method accept any object type as parameter

I want to pass 2 different object types to a method to update the view. How can I have this method
accept 2 different object types and access them instead of having 2 different methods for 2 different object types.
I needed something like this -
fun updateView(object: Any<T>) {
//Access the objects here to update the view
}
fun <T : Any> updateView(obj: T) {
//Access the objects here to update the view
}
OR
fun updateView(obj: Any ?= null, obj2:Any ?= null) {
// Access the objects here to update the view
// pass check nullity and use which you want (or not null), other parameter will remain null
obj?.let {
it...
}
obj2?.let {
it...
}
}
Call
updateView(obj1, obj2)
// OR
updateView(obj2 = myObj2)
You can use interfaces for this:
interface ViewInterface {
fun action()
}
class ObjectA : ViewInterface {...}
class ObjectB : ViewInterface {...}
fun updateView(ob: ViewInterface) {
ob.action()
}
try something like this
fun updateView(variable1: Any? = null, variable2:Any? = null) {
//Access the objects here to update the view
}
using named parameters, you can then just set the variables you need when calling the method:
updateView(variable1 = "something")
updateView(variable2 = "something else")
Have your 2 objects implement the same interface or inherit from the same superclass then do something like:
fun updateView(object: MyInterface) {
...
}
Use polymorphism
fun updateView(object: X) {
...
}
fun updateView(object: Y) {
...
}
You can pass two types of object by this way
fun updateView(data:Any? = null,data2:Any?=null) {
//Cast Your Object To your desired type and also can pass null too
// Access the objects here to update the view
}
I would advise separating the two functions, or to use inheritance of some sort to use a single function. But as I see none of those above (which are correct, from the SOLID point of view) satisfies your request, you can just check inside the function based on class.
fun updateView(object: Any) {
when(object){
is Class1Type -> // do whatever fits for the first case
is Class2Type -> // do whatever fits for the second case
else -> // etc.
}
}
The best solution is not related to Kotlin at all. Just make both of them implement an interface and use this interface as the function parameter type.
In general, accepting Any as an input type is not a good practice and using generics is an overkill.
interface DoesStuff
class DoesStuffA: DoesStuff { }
class DoesStuffB: DoesStuff { }
fun doStuff(doer: DoesStuff) {
// do stuff
// if need to distinguish between types
when (doer) {
is DoesStuffA -> // do A
is DoesStuffB -> // do B
}
}

Unit testing coroutines on UI thread

I'm using coroutines to do an asynchronous call on pull to refresh like so:
class DataFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
// other functions here
override fun onRefresh() {
loadDataAsync()
}
private fun loadDataAsync() = async(UI) {
swipeRefreshLayout?.isRefreshing = true
progressLayout?.showContent()
val data = async(CommonPool) {
service?.getData() // suspending function
}.await()
when {
data == null -> showError()
data.isEmpty() -> progressLayout?.showEmpty(null, parentActivity?.getString(R.string.no_data), null)
else -> {
dataAdapter?.updateData(data)
dataAdapter?.notifyDataSetChanged()
progressLayout?.showContent()
}
}
swipeRefreshLayout?.isRefreshing = false
}
}
Everything here works fine when I actually put it on a device. My error, empty, and data states are all handled well and the performance is good. However, I'm also trying to unit test it with Spek. My Spek test looks like this:
#RunWith(JUnitPlatform::class)
class DataFragmentTest : Spek({
describe("The DataFragment") {
var uut: DataFragment? = null
beforeEachTest {
uut = DataFragment()
}
// test other functions
describe("when onRefresh") {
beforeEachTest {
uut?.swipeRefreshLayout = mock()
uut?.onRefresh()
}
it("sets swipeRefreshLayout.isRefreshing to true") {
verify(uut?.swipeRefreshLayout)?.isRefreshing = true // says no interaction with mock
}
}
}
}
The test is failing because it says that there was no interaction with the uut?.swipeRefreshLayout mock. After some experimenting, it seems this is because I'm using the UI context via async(UI). If I make it just be a regular async, I can get the test to pass but then the app crashes because I'm modifying views outside of the UI thread.
Any ideas why this might be occurring? Also, if anyone has any better suggestions for doing this which will make it more testable, I'm all ears.
Thanks.
EDIT: Forgot to mention that I also tried wrapping the verify and the uut?.onRefresh() in a runBlocking, but I still had no success.
If you want to make things clean and consider using MVP architecture in the future you should understand that CourutineContext is external dependency, that should be injected via DI, or passed to your presenter. More details on topic.
The answer for your question is simple, you should use only Unconfined CourutineContext for your tests. (more)
To make things simple create an object e.g. Injection with:
package com.example
object Injection {
val uiContext : CourutineContext = UI
val bgContext : CourutineContext = CommonPool
}
and in test package create absolutely the same object but change to:
package com.example
object Injection {
val uiContext : CourutineContext = Unconfined
val bgContext : CourutineContext = Unconfined
}
and inside your class it will be something like:
val data = async(Injection.bgContext) {service?.getData()}.await()

Kotlin and idiomatic way to write, 'if not null, else...' based around mutable value

Suppose we have this code:
class QuickExample {
fun function(argument: SomeOtherClass) {
if (argument.mutableProperty != null ) {
doSomething(argument.mutableProperty)
} else {
doOtherThing()
}
}
fun doSomething(argument: Object) {}
fun doOtherThing() {}
}
class SomeOtherClass {
var mutableProperty: Object? = null
}
Unlike in Java, where you could be left alone to worry about null dereferencing at runtime, this doesn't compile - quite rightly. Of course, mutableProperty may no longer be null once within the 'if'.
My question is what's the best way to handle this?
A few options are apparent. Without using any new Kotlin language features, the simplest way is obviously to copy the value to a method-scope one that won't subsequently change.
There's this:
fun function(argument: SomeOtherClass) {
argument.mutableProperty?.let {
doSomething(it)
return
}
doOtherThing()
}
This has the obvious disadvantage that you need to return early or otherwise avoid executing the subsequent code - OK in certain, small contexts, but has a smell to it.
Then there's this possibility:
fun function(argument: SomeOtherClass) {
argument.mutableProperty.let {
when {
it != null -> {
doSomething(it)
}
else -> {
doOtherThing()
}
}
}
}
but whilst it has greater clarity of purpose, arguably it's more unwieldy and verbose than the Java-style way of dealing with this.
Am I missing anything, and is there a preferred idiom with which to achieve this?
Update:
As mentioned by franta on the comments, if the method doSomething() returns null, then the code on the right side of the elvis operator will be executed, which might not be the desired case for most. But at the same time, in this case, it is very likely that the doSomething() method will only do something and not return anything.
And an alternative: as protossor has mentioned on the comments, also can be used rather than let, because also returns this object instead of the result of the function block.
mutableProperty?.also { doSomething(it) } ?: doOtherThing()
Original answer:
I would use let with Elvis operator.
mutableProperty?.let { doSomething(it) } ?: doOtherThing()
From the doc:
If the expression to the left of ?: is not null, the elvis operator
returns it, otherwise it returns the expression to the right. Note
that the right-hand side expression is evaluated only if the left-hand
side is null.
For a block of code after the right-hand side expression:
mutableProperty?.let {
doSomething(it)
} ?: run {
doOtherThing()
doOtherThing()
}
I don't believe there is a really "short" way to achieve it, however you can simply use a conditional within with or let:
with(mutableVar) { if (this != null) doSomething(this) else doOtherThing() }
mutableVar.let { if (it != null) doSomething(it) else doOtherThing() }
In fact, "capturing" a mutable value is one of the main use cases of let.
This is equivalent to your when statement.
There is always the option you described, assigning it to a variable:
val immutable = mutableVar
if (immutable != null) {
doSomething(immutable)
} else {
doOtherThing()
}
which is always a nice fallback in case e.g. things get too verbose.
There probably isn't really a very nice way to achieve this because only the last lambda argument is allowed to be put outside the (), so specifying two wouldn't really fit the syntax of all of the other standard functions.
You could write one if you don't mind that (or if you'll be passing method references instead):
inline fun <T : Any, R> T?.ifNotNullOrElse(ifNotNullPath: (T) -> R, elsePath: () -> R)
= let { if(it == null) elsePath() else ifNotNullPath(it) }
...
val a: Int? = null
a.ifNotNullOrElse({ println("not null") }, { println("null") })
Note that I would personally not do this, because none of these custom constructs are very pleasant to read. IMO: stick with let/run and fall back to if-else when necessary.
What about:
argument.mutableProperty
?.let { doSomething(it) }
?: doOtherThing()
add custom inline function as below:
inline fun <T> T?.whenNull(block: T?.() -> Unit): T? {
if (this == null) block()
return this#whenNull
}
inline fun <T> T?.whenNonNull(block: T.() -> Unit): T? {
this?.block()
return this#whenNonNull
}
then you can write code like this:
var nullableVariable :Any? = null
nullableVariable.whenNonNull {
doSomething(nullableVariable)
}.whenNull {
doOtherThing()
}
I usually just do:
when(val it=argument.mutableProperty) {
null -> doOtherThing()
else -> doSomething(it)
}
i usually write it like this:
takeIf{somecondition}?.also{put somecondition is met code}?:run{put your else code here}
note the question mark after takeIf is a MUST. you can use also or apply keyword.
Thanks to #zyc zyc , now I use this code
inline fun <T> T?.ifNull(block: () -> Unit): T? {
if (this == null) block()
return this#ifNull
}
inline fun <T> T?.ifNonNull(block: (T) -> Unit): T? {
this?.let(block)
return this#ifNonNull
}
// use
xxxx.ifNull {
// todo
}.ifNonNull {
// todo
}
You could also do something like this:
class If<T>(val any: T?, private val i: (T) -> Unit) {
infix fun Else(e: () -> Unit) {
if (any == null) e()
else i(any)
}
}
You can then use it like this:
If(nullableString) {
//Use string
} Else {
}

Categories

Resources