I'm trying to make a nice SAM-like API for instantiating abstract classes because I don't like object expressions. I'm trying to do something like:
{my lambda code here}.my_extension_function()
Is this possible with Kotlin?
Yes it is possible. The sample code is:
// extension function on lambda with generic parameter
fun <T> ((T) -> Unit).my_extension_function() {
// ...
}
// extension function on lambda without generic parameter
fun (() -> Unit).my_extension_function() {
// ...
}
And use it, for example, like this:
// lambda variable with generic parameter
val lambda: (Int) -> Unit = {
// 'it' contains Int value
//...
}
// lambda variable without generic parameter
val lambda: () -> Unit = {
//...
}
lambda.my_extension_function()
// also we can call extension function on lambda without generic parameter like this
{
//...
}.my_extension_function()
// or with generic parameter
{ i: Int ->
//...
}.my_extension_function()
Note: if you call extension function on lambda without creating a variable and there is a function call before it you need to add semicolon after function call, e.g.:
someFunction();
{
//...
}.my_extension_function()
Related
Trying to call lambda provided by MyClass constructor using Kotlin Reflection.
data class MyClass(
var magic:Int=2,
var lambdaValue: ()->String = { //trying to call this lambda from reflection
"Working"
},
)
fun main(args: Array<String>) {
val clazz=MyClass::class
val obj=clazz.createInstance()
val kProperty=clazz.memberProperties
clazz.constructors.forEach{cons-> // for each construtor
cons.parameters.forEach{ parameter-> // looping through constructor parameters
val property=kProperty.find { it.name==parameter.name } // finding the exact property
print(parameter.name+" : ")
if(parameter.type.arguments.isEmpty()) // if empty Int,Float
{
println(property?.get(obj))
}else{
println(property?.call(obj)) // unable to call lambda
}
}
}
}
property.call(obj) returns Any which is not invokable. Any solution?
Expected:
magic : 2
lambdaValue : Working
Frankly speaking, I'm not sure what was your idea behind parameter.type.arguments.isEmpty(). It seems unrelated to what you try to do.
If we have a value of the property already, we can simply check its type and if its is a function then invoke it:
val value = kProperty.find { it.name==parameter.name }!!.get(obj)
print(parameter.name+" : ")
when (value) {
is Function0<*> -> println(value())
else -> println(value)
}
I think usefulness of such a code in generic case isn't very high. This code doesn't know what is the function and if it is going to return a value or perform some action, etc. Maybe in your specific case it is more useful.
I am trying to do something like the following:
class Event<TPayload>() {
fun subscribe(handler: (payload: TPayload) -> Unit) { ... }
fun subscribe(handler: () -> Unit) where TPayload : Unit { ... }
}
The intention is that instances of Event<Unit> will have two overloads of subscribe(), but other instances will only have one.
The above code will not compile. I tried using extension methods, but would have to use a different name for the extra method, rather than overloading it.
You can define that second function as an extension function so it only appears for Events who have a type of Unit. It's okay to overload the function name. Define it outside the class:
inline fun Event<Unit>.subscribe(crossinline handler: ()->Unit) =
subscribe { handler() }
Test:
class Event<T> {
private val subscribers = mutableListOf<(T)->Unit>()
fun subscribe(handler: (payload: T) -> Unit) {
subscribers += handler
}
fun send(payload: T) {
for (subscriber in subscribers) subscriber(payload)
}
}
fun main() {
val event = Event<Unit>()
// Using verbose syntax to prove it's the extension function being used
// and not a lambda with implicit 'it':
event.subscribe(fun() { println("got unit") })
event.send(Unit)
}
If you use a lambda, the compiler will use the first subscribe function with an implicit it parameter since it takes precedence in overload resolution. But runtime behavior would be the same either way if you aren't using the parameter.
I've looked at the source code of let function:
#kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
i clearly understand that using it keyword inside of the block (code) i'm sending to
let function will refer to the object that called the let function.
But on which object does this keyword will refer if i will use it inside the block (code).
The way i see it, it should be the same object that it refer to, but i'm not sure.
My computer gone bad so i can't test it by myself.
Can someone not only give me the answer but also will explain me the logic behind the this keyword in the situation above?
example of code:
val p:Preson = Person()
p.let{**this**}
this in the let lambda (block) means the same thing as it does outside the let lambda. The let lambda does not suddenly "change" what this means.
For example:
class Foo {
fun foo() {
// here, "this" refers to the receiver of foo(), i.e. some instance of Foo
1.let {
// here, "it" refers to 1,
// and "this" refers to the receiver of foo() too
}
}
}
fun main() {
// here, "this" does not mean anything, main() has no receiver
2.let {
// here, "it" refers to 2,
// and "this" does not mean anything either
}
}
This is because this refers to the receiver of a function, but the function type of the parameter that let takes has no receiver:
public inline fun <T, R> T.let(block: (T) -> R): R {
^^^^^^^^
Compare that to run, which is very similar to let, except its receiver as the receiver of the block parameter:
public inline fun <T, R> T.run(block: T.() -> R): R {
return this.block() // instead of block(this) like let
}
which means that:
1.let {
// here, "this" refers to 1
// "it" does not mean anything because the function type T.() -> R has no parameters
}
Another example that uses both this and it:
fun usesBoth(x: Int, y: String, block: Int.(String) -> Unit) {
x.block(y)
}
If you call usesBoth with a lambda,
usesBoth(1, "x") {
println(this) // prints 1
println(it) // prints x
}
this in the lambda would refer to 1, because block is called on x in usesBoth, and it would refer to "x", because y is passed to block as a parameter.
this is just one of parameters (called a "receiver") that could be passed to the lambda by a calling code. If lambda doesn't support a receiver, as in the let example, this will be just this of the enclosing code:
class Foo {
fun foo() {
val p = Person()
p.let{ this } // `this` is `Foo`
}
}
If lambda has a receiver then it depends what will be passed to it as this. For example, a very similar function to let, but passing the object as this is apply:
class Foo {
fun foo() {
val p = Person()
p.apply{ this } // `this` is `Person`
}
}
Of course, this doesn't have to be the object that we invoked the function on. It could be just any object, it depends on what the function will pass to the lambda. For example, in Kotlin stdlib we have a buildString() function that instantiates a StringBuilder, runs the lambda passing the StringBuilder as this and then returns a string that was built by the lambda. We use this function like this:
val s = buildString {
// `this` is `StringBuilder`
append("hello") // implicit this.append()
append("world")
}
By looking into buildString() signature we can see that the lambda receives StringBuilder as its receiver:
public inline fun buildString(builderAction: StringBuilder.() -> Unit): String {
You can read more about lambdas with receivers in the documentation: https://kotlinlang.org/docs/lambdas.html
As in Kotlin function is considered data type also ,I’m wondering is it possible to make that function injectable and then can I pass that to other method.
What I mean is to make this function injectable
fn: (data) -> Component and then I will pass this to method
Yes you can reference a function using :: operator that will create a KFunction1<data, Component> which is considered as (data) -> Component or data.() -> Component in kotlin.
Example:
fun acceptLambda(block: (String) -> Unit) {
block("Test")
}
// Create function having same argument and return type
// Both of them in same package won't work use either of these
fun String.t1() {
println(this)
}
fun t2(s: String) {
println(s)
}
// Call them, by passing the reference
test(String::t1) // Prints: Test
test(::t2) // Prints: Test
Thing to be noted is that if you create an extension function as a member function in any class (just like t1) then it cannot be referenced, so I'll recommend using functions like t2 instead.
Here is more info about how to reference constuctors, or memeber functions.
Please have a look here.
Kotlin functions are first-class, which means that they can be stored
in variables and data structures, passed as arguments to and returned
from other higher-order functions. You can operate with functions in
any way that is possible for other non-function values.
Example:
fun takeFunction(func: (Int) -> Component) {
val component = func(1)
}
fun foo(data: Int) : Component = TODO()
fun main() {
takeFunction({ if(it == 1) Component else error("wrong data") })
takeFunction(::foo)
takeFunction { foo(it) }
}
I have an interface
private interface WithTokenExecutor<T> {
fun execute(token: Token): Single<T>
}
and a function withToken that asynchronously gets an access token then returns the executed WithTokenExecutor parameter with the new token.
private fun <T> withToken(executor: WithTokenExecutor<T>): Single<T> {
return essentialApiTokenProvider.getTokenObservable(true) // returns an observable with the token
.flatMap { token -> executor.execute(token)) }
}
Then I to call the function with the:
fun getAppData(apps: List<String>): Single<AppsList> {
return withToken(object : WithTokenExecutor<AppsList> {
override fun execute(token: Token): Single<AppsList> {
return api.getDetails(token) // retuns a Single<AppsList>
}
})
}
This works, so my question is is it possible to change the return statement from an anonymous class to lambda even if the return type of the withToken and the WithTokenExecutor functions are generic?
I have tried doing this:
return withToken({ token -> api.getDetails(token) })
but the compiler says:
Type inference failed: fun <T> withToken(executor: StoreManager.WithTokenExecutor<T>):Single<T> cannot be applied to ((???) -> Single<AppsList>)
Is there a way to explicitly define the return type of these functions while still keeping the lambda?
If you have the option of modifying your declaration of WithTokenExecutor to:
typealias WithTokenExecutor<T> = (t : Token) -> Single<T>
...you will be able to implement your getAppData like this:
fun getAppData(apps: List<String>): Single<AppsList> = withToken { api.getDetails(it) }
If changing the declaration is not possible, it seems like you are out of luck until Kotlin 1.4 as #Pawel points mentions in the comments.