Here is a simplified example. This syntax works :
Handler().post({Log.v(TAG, "test")})
However, if I define this function
private fun doWithRunnable(toRun:Runnable) {
// whatever
}
And call it like this
doWithRunnable({Log.v(TAG, "test")})
Then I get the following error:
Required: RunnableFound: () -> Int
Both signatures look quite the same to me... what's the big difference?
In your Java example, you're taking advantage of the Java-interop feature for SAM Conversions. Unfortunately, this does not currently work for Kotlin interfaces, as the designers have deemed it unnecessary (there is some discussion on this see here).
Instead, you'd want to do something like:
fun doWithRunnable(runnable: () -> Any) {
// call it as `runnable()`
}
You could also define a typealias for this if you wanted (which is as close Kotlin comes to SAM syntax):
typealias RunMe = () -> Any
fun doWithRunnable(runnable: RunMe) {
runnable()
}
Note that currently, typealiases have to be declared at the top level, they can't be declared inside a class or method.
Related
I want to execute a block of code after checking if user is authenticated or not. Something like this:
inline fun <T : Any, R> T?.isUserAuthenticated(callback: (T) -> R) {
FirebaseAuth.getInstance().currentUser?.let {
//Function call
} ?: kotlin.run {
FirebaseAuth.getInstance().signInAnonymously().addOnSuccessListener {
//Function call
}
}
This approach isn't working, but is there any alternative to this?
Inline functions in Kotlin should be used over regular functions when:
You desperately need to allocate memory more efficiently.
When a function accepts another function or lambda as an argument.
You need to prevent object creation and have better control flow.
Otherwise, inlining may cause the generated code to grow. Most likely there are also other situations when it is worth using inline functions but I only added a few (important) of them.
When it comes to checking if a user is authenticated or not, I would rather create a regular function that looks like this:
fun getAuthState() = auth.currentUser != null
And use it:
val isAuthenticated = getAuthState()
if(!isAuthenticated) {
auth.signInAnonymously().addOnCompleteListener(/*...*/)
}
Or if using Kotlin Coroutine:
if(!isAuthenticated) {
auth.signInAnonymously().await()
}
So it's one approach or the other.
I would add this function in a repository class so it can be used all across the entire project.
If you're interested in seeing some code, I recommend you check the following resource:
How to handle Firebase Authentication in clean architecture using Jetpack Compose?
And here is the corresponding repo.
This question is a derivative of a recent one I made here
While that one was resolved, I immediately faced a new one of a similar nature, but I am also not able to find a solution for it.
Sample example:
abstract class Endpoint<T>() {
private val myList: MutableList<(T) -> Unit> = mutableListOf()
fun <E : T> addToList(cbk: (E) -> Unit) { <-- E extends T
myList.add(cbk)
}
}
Usage example would be
Some sealed class
sealed class MainSealedClass {
data class ChildClass(val someParam: Int): MainSealedClass()
}
And the function call
anEndpointInstance.addToList<ChildClass>{it: ChildClass ->
// do something here
}
I tried doing the following, but it looks like it is not is not allowed
val myList: MutableList<(out T) -> Unit> <--- Unsupported error shows up
Is there a way to do this without having to add an extra declaration at the class level?
I considered using inline reified but the function needs to access private fields of the Endpoint instance, so I do not want to use that option.
I don't think the language supports specifying variance of the parameters of a functional type, or references to functions with generic types that aren't defined. For example, if you want to get a reference to a generic function, you have to specify its generic types in the functional type declaration.
There is dubious usefulness here anyway. If you had a list of functions that each have different input types, then when you retrieve a function from the list, you won't be able to call it because you won't know which input type it supports. You might as well have a MutableList<Any>.
Thanks to Tenfour04's knowledge, I was able to find a workaround to my issue. I do not consider this to be a fix for my issue, but given that it apparently cannot be done as I expected, the next best thing was to work around the issue with the help of a wrapper.
Here is how my code turned out with the work using the sample in my question
abstract class Endpoint<T>() {
private val myList: MutableList<(T) -> Unit> = mutableListOf()
fun add(cbk: (T) -> Unit) {
myList.add(cbk)
}
inline fun <reified N : T> addToList(crossinline callback: (N) -> Unit) {
add { if (it is N) callback.invoke(it) }
}
}
And its usage is the way I wanted in my question, nothing changes at this point.
run signature looks like:
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
The way I see it, the callback (block), is being called via a receiver but without any parameters. So how someone (with a lot of credits here) , said me that:
1.
thing.run(::println)
is equivalent to :
2.
thing.run { println(this) }
I can't understant how the first is running at all... since the way i see it the println won't get any parameter
When you pass a function to a higher-order function by using the function reference syntax instead of a lambda, it doesn't matter if there's a receiver or not. The receiver is like any other parameter, and can be thought of as the first parameter.
So the syntax of run's function parameter:
T.() -> R
will be treated exactly the same as the syntax of let's function parameter:
(T) -> R
so ::println can match either of these.
This works in the other direction, too. When you pass an extension function or member function using function reference syntax, the receiver of the extension function is treated as the first parameter. Either of these is valid:
val list = listOf(1, 2, 3)
list.run(List<Int>::sum)
list.let(List<Int>::sum)
So whether there is a receiver or not only affects lambdas. The actual function signature is the same. You can see this issue if you try to define two functions like this. There will be a compiler error for the two functions having the same signature:
class Foo
fun bar(foo: Foo) {
println("Hello")
}
fun Foo.bar() {
println("Hello")
}
Today while programming I found some odd behaviour in Kotlin. I could easily go around it, but I wonder if there is some reason to it or if it is a bug in Kotlin.
I have the following interface of a delegate which delegates the showing of a dialog to the Activity.
interface ViewModelDelegate {
fun showWarningDialog(textResource: Int)
}
I want to implement it as following in the Activity. Since I know I can only do it with a context and the Activity.getContext() may return null, I wrap the code in context?.let
override fun showWarningDialog(textResource: Int) = context?.let {
//show dialog
}
However this gives me a compile error:
Return type of 'showWarningDialog' is not a subtype of the return type of the overridden member 'public abstract fun showWarningDialog(textResource: Int): Unit defined in com.some.class.path'
Which really confused me, because I don't want to return anything. So since let returns whatever the function inside returns, I was wondering if I could fix it by writing a version of let which does not return anything.
fun <T, R> T.myLet(block: (T) -> R) {
let(block)
}
However this did not remove the compiler error. I found then that the mouseover text over the error gives more information (would be nice if the compiler did). It says:
Return type is 'Unit?', which is not a subtype of overridden
Now that tells me more about the problem. Because the function context?let call may not happen, it could return null. Now there are multiple ways to go around this. I could add ?: Unit too the end of the function call or I could define showWarningDialog to return Unit? which will allow me to call it just fine in most cases. However none of these solutions are desireable. I will probably just make a normal method and call the let inside of that instead of delegating the call to it. Costs me another level of indentation and an extra vertical line:
override fun showWarningDialog(textResource: Int) {
context?.let {
//show dialog
}
}
My question is, is this behaviour intended? Why or when would this be useful that a function that returns Unit cannot be delegated to an optional function call. I am very confused by this behaviour.
Single expression function
fun foo() = <expression>
by language design is equivalent to
fun foo(): <ReturnType> {
return <expression>
}
And because Unit? is not a not a subtype of Unit, you can't return it in from a function, which returns Unit. In this sense Unit just another type in the type system, it's not something magical. So it works just as it's supposed to work with any other type.
Why or when would this be useful that a function that returns Unit cannot be delegated to an optional function call.
So basically the question is why language designers did not created a special handling to accept Unit? from a function declaring Unit as a return type. I can think about a few reasons:
It requires to create this special handling in the compiler. Special cases lead to bugs, break slim language design and complicate documentation.
As it had to be a special case, it would be not really clear and predictable for programmers. Currently it works in the same way for all types, no special treatments. It makes the language predictable, you don't need to check the documentation for every type to see if it's treated specially.
It also adds some additional safety, so to make you notice that your expression can actually skip the calculation.
So trying to summarize, I would say making this case work does not add much of value but can potentially bring some issues. That's probably why they did not add it to the language.
lets discuss this case when you have return type for example String
interface someInterface{
fun somFun():String
}
class someClass : someInterface {
var someString:String? = null
override fun somFun()=someString?.let {
//not working
it
}
override fun somFun()=someString?.let {
//working
it
}?:""
}
so what we see that when parents return type is String you cannot return Strin? it is jus kotlins nullSafety ,
what is different when you don't have return type ? lets change the code above a little
interface someInterface{
fun somFun():String
fun unitFun()
}
class someClass : someInterface {
var someString:String? = null
override fun unitFun() {
//if it is possible to return null in here
}
override fun somFun()=someString?.let {
val someresult = unitFun().toString() //you will get crash
it
}?:""
}
now we have another function without return type (unitFun Unit)
so if you can return Unit? in your subclass it will cause a crash when you want to use the result of method because it is defined asUnit and you dont need any null checks.
generally it means Unit is also type and you need to keep it null safe .
We are trying to understand calling a function in Kotlin
The function looks like this
fun onSIMPLE(view: View){
val snack = Snackbar.make(view,"This is a simple Snackbar", Snackbar.LENGTH_LONG)
snack.show()
}
And the call is made this way
btnSB2.setOnClickListener {onSIMPLE(it)}
What we do not understand is how does one know to use the keyword "it"?
Who ever created the keyword "it" must have never searched the Web
We plugged every reasonable keyword in the ( ) to solve the issue
YES we also looked at the documentation
Is there a better way to construct the function or make the call?
it is the implicit name for a single parameter lambda. You can override as you wish, e.g:
btnSB2.setOnClickListener { view -> onSIMPLE(view)}
setOnClickListener expects a lambda as a parameter, using a Java-like approach, this should look like this:
btnSB2.setOnClickListener({
v:View -> onSIMPLE(it)
})
Also, if the lambda is the last parameter for a given function, it can be specified outside of the parenthesis, which would look like this:
btnSB2.setOnClickListener {
v:View -> onSIMPLE(it)
}
It is common for lambda functions to have a single parameter. For these functions, Kotlin maintains the it keyword. Knowing this, the code becomes:
btnSB2.setOnClickListener {
onSIMPLE(it)
}