Kotlin: Force inline method to preserve the amount of defined lines - android

So I have this method that I want to use for logging while also returning the logged object:
inline fun <T> T.btwLog(prefix:String="")=apply { Timber.d("$prefix->${toString()}") }
The Timber class is from a logging Library and also (when set up this way) shows the line at which it was called, which is also why the method is inline (to show the line and correct class of where it was called)
For example:
val b = someThingComplex().btwLog()
This basically works, the only problem is that the line number is wrong. This basically has to be because Kotlin inserts the method and while doing so adds some lines to the class (4)?.
So the example from before probably looks similar to this:
val b = someThingComplex()
.apply {
Timber.d("$prefix->${toString()}")
}
An alternative is this method where Kotlin doesn't add additional lines:
inline fun Any.log(s:String="")= Timber.d(s+this)
Is there any way that I could force Kotlin to just inline it exactly as I wrote it and not add linebreaks and stuff? Or is there a better way that I could define the method in general?
Thanks.

I suspect that the shifting of line numbers might be due to the call to apply. Try it this way.
inline fun <T> T.btwLog(prefix: String = ""): T {
Timber.d(prefix + "->" + this)
return this
}

Related

Why do we use inline keyword on a variable in kotlin?

What I can understand from google documentation is that we can use inline keyword to enhance the performance and reduce the memory allocation on runtime in case of high order function but why in this case we are inlining a variable
#PublishedApi
internal inline val <T : Any> T.loggingTag: String
get() {
val tag = this::class.java.name.substringAfterLast(".")
if (tag.contains("$")) {
return tag.substringBefore("$")
}
return tag
}
in case you are curious the logging tag variable is used in an inline function
inline fun <reified T : Any> T.errorLog(error: Throwable? = null, message: String? = null) {
errorLog(loggingTag, error, message)
}
also the errorLog function inlined even it doesn't take an other function as a parameters does anyone able to explain for me this ?
inline fun errorLog(tag: String, error: Throwable? = null, message: String? = null) {
Timber.tag(tag).e(error, message)
}
That val isn't a variable. It's a property. If you see val or var outside of a function, it's a property, not a variable.
Properties have getter functions. In your example, the getter function is what is being marked as inline.
There are two main reasons to use inline functions:
In a higher order function, it avoids allocating a function object to represent the function parameter (lambda or function reference) being passed to it. It also allows lambdas that are passed to use keywords that are normally restricted to local use, such as return, break, and continue.
It is necessary for reified generics. If a function needs to be able to inspect one of its generic types, the type needs to be marked as reified, which in turn requires the function to be marked inline.
Generally, you want to avoid using inline if a function doesn't fit one of the above cases, because actual function calls themselves are rather trivial, and if you inline a function that is used frequently (the only kind of function you would want to be optimized), then its code is going to be repeated everywhere it's used in your compiled app, increasing the size of the app.
An exception might be if the inline function contains only one function call, so it's not really increasing code size.
I do not see any reason the inline keyword is needed for the extension property in your first block of code. It is not a higher-order function, and it is a multi-line function. I also don't see why it is defined as a generic function, since the type T is not used for anything inside the function. I think I would define it as:
internal val Any.loggingTag: String
Also, in your second block of code, I don't see any reason why there is a generic type, since it is not used in that function. It could be defined as:
fun Any.errorLog(error: Throwable? = null, message: String? = null) {
The third block of code doesn't benefit much from inlining either. It's not a higher-order function. If function call overhead is that much of an issue for performance, it would make more sense to fork Timber and rewrite how it creates tags, because using custom temporary tags like that in Timber involves a few method calls under the hood.
The inline keyword is used to make functions inline, meaning that when a function is called, its body is copied and pasted into the caller's code instead of making a function call. This allows for reduced execution time, as there is no need to jump to another function and back. Inline functions can also be used to reduce code size and improve readability.

Kotlin inline function for checking if user is autheticated or not?

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.

Return type is 'Unit?', which is not a subtype of overridden

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 .

Passing a function that requires parameters as a callback in Kotlin

I am using Kotlin (for Android development) and I'm trying to pass a function to another function which I'd like to use as a callback. The code is very basic as this is just a test for now.
Please note that, although you will probably wonder why I'm using a callback like this, it's just for test purposes. In my actual application I would want to assign the callback to a value and call it later on once an asynchronous method has completed.
I cannot use co-routines etc... since this code will be used for a multi-platform solution, hence my interest in making a function callback.
My Kotlin Class that will receive the function (callback)
class SampleApi {
private var counter: Int = 0
fun startCounting(initialValue: Int, counterCallBack: (resultVal: Int) -> Unit) {
counter = initialValue
counter++
counterCallBack(counter)
}
}
The above is a basic class that has a function startCounting which will receive an integer and a function. It will then call that function and pass in a value.
The calling code
class MainActivity : AppCompatActivity() {
private val sampleApi: SampleApi = SampleApi()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sampleApi.startCounting(5, {counterCallBack()})
}
private fun counterCallBack(counter: Int) {
Toast.makeText(this, counter.toString(), Toast.LENGTH_SHORT).show()
}
}
The Sample code shown above contains the callback method (which is expecting to receive an integer), and contains the call to the startCounting method from the SampleApi class that is expecting to receive a function.
The problem I have is this line:
sampleApi.startCounting(5, {counterCallBack()})
The error within Android Studio is due to the fact that a value is that the function is expecting an integer and hence I receive the error:
No value passed for parameter 'counter'
I tried to look at lambdas but didn't think that was the issue. I have searched to see if an answer to this already existed and, whilst helpful they didn't seem to consider the same use case as mine.
Any help with this would be very much appreciated.
Because counterCallback has exactly the type you need, you can also use a function reference instead of a lambda:
sampleApi.startCounting(5, ::counterCallBack)
I assume what you want to do is create a toast displaying the Int every time your callback lamda is called from the SampleApi.
You just need to make use of the Int that your lamba is called with, using it:
sampleApi.startCounting(5, {counterCallBack(it)})

Kotlin lambda compiles against java API but not against my own function

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.

Categories

Resources