Android Kotlin smart operator overloading - android

What if overload some Kotlin operator and use it like this:
// Inits somewhere before usage.
val someStrFromServer: String?
lateinit var myFieldText: TextView
override fun onStart() {
super.onStart()
myFieldText.text = someStrFromServer / R.string.app_none
}
Operator overloading:
operator fun String?.div(resId: Int): String {
return if (this.isNullOrBlank()) {
context.getString(resId)
} else {
this
}
}
Output if someStrFromServer null:
D/DEBUG: None
Output if someStrFromServer not null:
D/DEBUG: someStrFromServer
Does anyone know, if in Kotlin exists a more efficient and short way to handle this? Perhaps, even more, global, like extension function.

You can do that, but it's not very intuitive because the div is normally used in mathematical calculations only. I'd recommend to use something like
someStrFromServer.takeUnless { it.isNullOrBlank()} ?: context.getString(resId)
Or simplified via extension
fun String?.fallback(resId: Int) = takeUnless { it.isNullOrBlank()} ?: context.getString(resId)
used like this:
myFieldText.text = someStrFromServer.fallback(R.string.app_none)

Related

How to write Composable function using Kotlin Functional (SAM) Interface

We can write functional interfaces in Kotlin like this - function-interfaces
fun interface Sum {
fun add(a: Int, b: Int): Int
}
val sumImpl = Sum { a, b ->
return#Sum a + b
}
val testSum = sumImpl.add(4, 5)
How can we write Jetpack Composable function in same way? Below code is not working.
`
fun interface SampleText {
#Composable
fun text(data : String)
}
val textImpl = SampleText { data ->
return#SampleText #Composable { Text(data) }
}
#Composable
fun testText() = textImpl.text("Data")
I have tried this as well, but this also didn't work.
fun interface SampleText {
fun text(data : String) : #Composable () -> Unit
}
val textImpl = SampleText { data ->
#Composable { Text(data) }
}
#Composable
fun testText() = textImpl.text("Data")
The first version is not compiling in its lambda form because your interface function returns a Unit and your'e actually having a Type mismatch error, its just weird the compiler reports Internal Error when you try to return a #Composable annotated function, but the issue becomes clear if you simply return something like a String.
vs
To solve your first version, either you fully declare an object of the class like this (though its useless since you want a lambda version of your SAM interface not an actual object in the first place)
val textImpl = object: SampleText {
#Composable
override fun text(data: String) {
Text(data)
}
}
, but it will work just by simply calling the testText() function like this.
testText()
Or change it to your second version.
Now for your second version, since your interface returns a #Composable lambda, you have to invoke it as well in the call-site, making two function invocations to make it work,
testText()() // two invocations
first call invokes your testText() function, second pair of parenthesis invokes the #Composable lambda from your interface.
Or simply call .invoke()
testText().invoke() // just call the .invoke() of the returned composable lambda
Either of the implementations and calls display the text "Data"

How to use the keyword also in kotlin android

Am learning android kotlin follow this:
https://developer.android.com/topic/libraries/architecture/viewmodel#kotlin
class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData<List<User>>().also {
loadUsers(it)
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
Dont know how to write the fun loadUsers()
Here is my User:
class User {
constructor(name: String?) {
this.name = name
}
var name:String? = null
}
If dont use the keyword 'also' , i know how to do it.
But if use 'also' , it seems not work.
Here is how i try to write the fun loadUsers:
private fun loadUsers( it: MutableLiveData<List<User>>){
val users: MutableList<User> = ArrayList()
for (i in 0..9) {
users.add(User("name$i"))
}
it = MutableLiveData<List<User>>(users)
}
Error tips near it : Val cant be ressigned
Part 1: According to the Kotlin documentation, also provides the object in question to the function block as a this parameter. So, every function call and property object you access is implied to refer to your MutableLiveData<List<User>>() object. also returns this from the function block when you are done.
Thus, another way of writing your MutableLiveData<> would be like this:
val users = MutableLiveData<List<User>>()
users.loadUsers()
Part 2: As far as how to implement loadUsers(), that is a separate issue (your question is not clear). You can use Retrofit + RxJava to load the data asynchronously, and that operation is totally outside of the realm of ViewModel or also.
Part 3: With your approach, you have conflicting things going on. Instead of doing a loadUsers() from your lazy {} operation, I would remove your lazy {} operation and create a MutableLiveData<> directly. Then, you can load users later on and update the users property any time new data is loaded. Here is a similar example I worked on a while ago. It uses state flows, but the idea is similar. Also use a data class to model the User instead of a regular class. Another example.
It is solved change to code:
private fun loadUsers( it: MutableLiveData<List<User>>){
val users: MutableList<User> = ArrayList()
for (i in 0..9) {
users.add(User("name$i"))
}
it.value = users
}
it can't be reassigned , but it.value could .

Why is the purpose of functional interfaces in Kotlin?

I am learning Kotlin and I came across Functional Interfaces here:
https://kotlinlang.org/docs/fun-interfaces.html#sam-conversions, and I don't quite understand the purpose of declaring and using an interface like in the example. The example is as follows:
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
// Creating an instance of a class
val isEven = object : IntPredicate {
override fun accept(i: Int): Boolean {
return i % 2 == 0
}
}
// Or creating an instance using lambda
val isEven = IntPredicate { it % 2 == 0 }
fun main() {
println("Is 7 even? - ${isEven.accept(7)}")
}
Now why do this when we can declare a function isEven and then call it whenever we want, like this:
fun isEven(i: Int): Boolean {
return i % 2 == 0
}
fun main() {
println("Is 7 even? - ${isEven(7)}")
}
And we get the same result. I think I am missing something here, can anybody help with an explanation.
Now I get what lambda expression is but what does exactly these lines of code do (I have seen before in other files but didn't understand it)
// Creating an instance of a class
val isEven = object : IntPredicate {
override fun accept(i: Int): Boolean {
return i % 2 == 0
}
}
Thanks!
Your question isn't really about Kotlin and functional interfaces, but about fundamental concepts of object oriented programming. The question you ask is: why do we need interfaces if we can just use normal classes/functions? It is to abstract software components; to decouple caller of the function and its implementation.
Imagine we have a function that is used to filter a list of integers. It doesn't know rules for filtering, it receives them from external code in the form of IntPredicate. It could be declared as:
fun filterList(list: List<Int>, predicate: IntPredicate): List<Int> = TODO()
interface IntPredicate {
fun accept(i: Int): Boolean
}
Because we have to provide an implementation of IntPredicate, using this function is quite cumbersome. For each use we need another implementation of IntPredicate. Even if we need to perform a very simple filtering, like looking for odd numbers. This seems like a much of work for such a simple case.
Functional interfaces and SAM conversions makes such cases much easier to use. After declaring predicate interface as fun interface IntPredicate, we can use our function like this:
filterList(list) { it % 2 == 0 }
Now, we can provide lambda instead of implementing a whole class.
Functional interfaces are not something entirely new, they won't let you do anything that would be not possible without them. By marking interface as fun we just enable a useful syntactic sugar for this interface.
Note that in practice my example does not make too much sense. We already have a very similar filter() function in stdlib, and in Kotlin we usually prefer function types over interfaces in such cases.

Get Enum type by mapping Enum value always complain null issue Android Kotlin

I have enum class and I am mapping by value, when I am return Enum value it always complain about null issue.
ConversationStatus.kt
enum class ConversationStatus(val status: String) {
OPEN("open"),
CLOSED("closed");
companion object {
private val mapByStatus = values().associateBy(ConversationStatus::status)
fun fromType(status: String): ConversationStatus {
return mapByStatus[status]
}
}
}
This always complain this issue. How can I fix this? Any recommendation for that. Thanks
There's 3 possible ways to go to.
Android Studio is often good at suggested fixes as you can see in the screenshot. It suggests to change the return type to ConversationStatus? which means it might return null. It will become this then:
companion object {
private val mapByStatus = values().associateBy(ConversationStatus::status)
fun fromType(status: String): ConversationStatus? {
return mapByStatus[status]
}
}
Another way is to tell the compiler that you ensure it will always not be null by adding !! to the return statement. Like this:
companion object {
private val mapByStatus = values().associateBy(ConversationStatus::status)
fun fromType(status: String): ConversationStatus {
return mapByStatus[status]!!
}
}
This will cause a crash though if you call the function with a status that's not "open" or "closed"
Alternatively you could provide a fall back value. With this I mean that it returns a default value in case you call the function with a string that's not "open" or "closed". If you want that to be OPEN you could do like this:
companion object {
private val mapByStatus = values().associateBy(ConversationStatus::status)
fun fromType(status: String): ConversationStatus {
return mapByStatus[status] ?: OPEN
}
}

Kotlin, generics, and a function that may have no arguments

I'm trying to genericise the boilerplate around a very common pattern, and Kotlin brings me tantalisingly close.
I've built a class that serves as a listener manager, as follows:
class GenericListenerSupport <EventArgumentType, ListenerFunction: (EventArgumentType) -> Unit> {
private val listeners = mutableListOf<ListenerFunction>()
fun addListener(listener: ListenerFunction) {
listeners.add(listener)
}
fun removeListener(listener: ListenerFunction) {
listeners.remove(listener)
}
fun fireListeners(argument: EventArgumentType) {
listeners.forEach { it.invoke(argument) }
}
}
and it can be used as follows:
class ExampleWithArgument {
private val listenerSupport = GenericListenerSupport<String, (String)->Unit>()
fun exampleAdd() {
listenerSupport.addListener({ value -> System.out.println("My string: "+value)})
}
fun exampleFire() {
listenerSupport.fireListeners("Hello")
}
}
So far, so good. But what if the listener has no arguments? Or stretching even further, multiple parameters.
I can scrape through with this:
class ExampleWithNoArgument {
private val listenerSupport = GenericListenerSupport<Nothing?, (Nothing?)->Unit>()
fun exampleAdd() {
listenerSupport.addListener({ System.out.println("I've got no argument")})
}
fun exampleFiring() {
listenerSupport.fireListeners(null)
}
}
but it smells, and obviously it's no use for multiple parameters.
Is there a better way to pull this off? e.g. something supporting this concept:
private val listenerSupport = GenericListenerSupport<???, (String, Double)->Unit>()
Since your GenericListenerSupport declares a type parameter EventArgumentType and expects an instance of it in fun fireListeners(argument: EventArgumentType), I doubt you can support multiple arguments in a clean way. Instead, I'd suggest using a data class (which is not so much extra code), as a clean and type-safe way to wrap multiple values:
data class MyEvent(val id: String, val value: Double)
private val listenerSupport = GenericListenerSupport<MyEvent, (MyEvent) -> Unit>()
As to passing no value, you can also use Unit, the type that has exactly one value Unit:
listenerSupport.fireListeners(Unit)
The type system and resolution won't allow you to pass no argument where a single one is expected, but, as #Ruckus T-Boom suggested, you can make an extension to fire listeners with no value where Unit is expected:
fun GenericListenerSupport<Unit>.fireListeners() = fireListeners(Unit)
A bit off-topic, but I think you can simplify the type if you don't need custom function types and (EventArgumentType) -> Unit is sufficient:
class GenericListenerSupport<EventArgumentType> {
/* Just use `(EventArgumentType) -> Unit` inside. */
}

Categories

Resources