Here, Handler is a function type.
And doSomething is one of such a handler function.
addHandler register it and give it a name.
Question is are there simpler way to convert a function doSomething to a lambda?
typealias Handler = (cmd: String, obj: Any?) -> Any?
fun doSomething(cmd: String, obj: Any?): Any? {...}
fun addHandler(name: String, handler: Handler) {...}
fun foo() {
addHandler("doSomething", { cmd, obj -> doSomething(cmd, obj) })
// or in other syntax
addHandler("doSomething") { cmd, obj -> doSomething(cmd, obj) }
}
Here, the phrase
{ cmd, obj -> doSomething(cmd, obj) }
is just converting a function to a lambda which has the same parameter sequence.
C++ has very simple syntax &doSomething for it. How about in Kotlin?
Kotlin also supports method references, in your case, you can do this:
addHandler("doSomething", ::doSomething)
Related
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
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.
With all the well-known single-function listeners we can use a simpler lambda notation
view.setOnClickListener { do() }
instead of the original, longer Java way of
view.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
do()
}
})
But what exactly makes this work? I tried to do the same with my own listener:
private var listener: OnCopyPasteClickListener? = null
interface OnCopyPasteClickListener {
fun onPasteClick(text: String)
}
fun setOnCopyPasteClickListener(onCopyPasteClickListener: OnCopyPasteClickListener) {
listener = onCopyPasteClickListener
}
and while the long approach works just fine:
copypaste.setOnCopyPasteClickListener(object : CopyPasteMenu.OnCopyPasteClickListener {
override fun onPasteClick(text: String) {
do(text)
}
})
I can't make it accept the short one:
copypaste.setOnCopyPasteClickListener {
do(it)
}
The IDE gives a type mismatch error.
Actually, if you have only one function to be invoked, I recommend you use Kotlin Callback.
typealias OnDoWorkListener = ((String) -> Unit)
class Work {
var doWork: OnDoWorkListener? = null
fun doSomething() {
doWork?.invoke("Message Here")
}
}
And in your function, you just set the callback to it
fun main() {
val work = Work()
work.doWork = {
Log.d("WORK", "This gets called from the `work` object. Message: $it")
}
work.doSomething();
}
We can also use function to set the listener as well.
class Work {
var doWork: OnDoWorkListener? = null
fun doSomething() {
doWork?.invoke("Message Here")
}
fun setOnWorkListener(listener: OnDoWorkListener) {
doWork = listener
}
}
fun main() {
val work = Work()
work.setOnWorkListener {
Log.d("WORK", "This gets called from the `work` object. Message: $it")
}
work.doSomething()
}
Higher order functions make this work:
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.
From the same page:
Passing a lambda to the last parameter
In Kotlin, there is a convention that if the last parameter of a
function accepts a function, a lambda expression that is passed as the
corresponding argument can be placed outside the parentheses:
val product = items.fold(1) { acc, e -> acc * e }
If the lambda is the only argument to that call, the parentheses can
be omitted entirely:
run { println("...") }
Knowing this, a possible update on your class would look like:
class CopyPaste {
private var listener: (String) -> Unit = {}
fun setOnCopyPasteClickListener(onCopyPasteClickListener: (String) -> Unit) {
listener = onCopyPasteClickListener
}
fun doCopyPaste(value: String) {
listener.invoke(value)
}
}
fun main() {
val copyPaste = CopyPaste()
copyPaste.setOnCopyPasteClickListener { println(it) }
copyPaste.doCopyPaste("ClipboardContent!")
}
The class CopyPaste stores the listener, which is a function that takes a String parameter and does not return anything. Its function setOnCopyPasteClickListener accepts a function with the same signature as the listener property and at the end doCopyPaste accepts a String parameter and passes it to the stored function.
Actually, just after I posted, I searched for more thoughts and found this thread: https://youtrack.jetbrains.com/issue/KT-7770 This is indeed a debated limitation as it currently only applies to Java, not Kotlin itself. There is also a suggestion there that gives almost the required simplicity:
interface OnCopyPasteClickListener {
fun onPasteClick(text: String)
companion object {
inline operator fun invoke(crossinline op: (text: String) -> Unit) =
object : OnCopyPasteClickListener {
override fun onPasteClick(text: String) = op(text)
}
}
}
and then, thanks to this overloaded operator, it can be called as:
copypaste.setOnCopyPasteClickListener(CopyPasteMenu.OnCopyPasteClickListener { text ->
do(text)
})
But as the suggested answers offer a more idiomatic solution, I'll accept one of those, I only wanted to include this approach here for reference.
I use a property observable delegate.
var state: State by Delegates.observable(START as State,
fun(prop: KProperty<*>, old: State, new: State) {
infix fun State.into(s: State): Boolean {
return this == old && s == new
}
when {
START into STOP -> {
doSomeMagic()
}
So, I use this infix function to make pretty look of comparing two values.
But if I want to make a library out of it, I need to move this infix function somewhere so there is no need to define it every time. But I can't figure out the way because it depends on two concrete values old and new. So I want it to look like this:
var state: State by Delegates.observable(START as State,
fun(prop: KProperty<*>, old: State, new: State) {
when {
START into STOP -> {
doSomeMagic()
}
And define into somewhere else.
This is possible but takes a little work and a bit of a structural change to how the observable delegate works.
First, create a class to hold the state on a change, this will allow you to also add your infix function to this class:
data class StateChange<T>(val property: KProperty<*>, val oldValue: T, val newValue: T) {
infix fun State.into(s: State): Boolean {
return this == oldValue && s == newValue
}
}
Now create a new delegate function that will create the delegate and instead of calling a lambda function with all the values as a parameter, will expect a lambda that is an extension method on the StateChange class. Therefore this lambda will have access to its properties and functions as well.
inline fun <T> observableState(initialValue: T, crossinline onChange: StateChange<T>.() -> Unit):
ReadWriteProperty<Any?, T> =
object : ObservableProperty<T>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) {
with (StateChange(property, oldValue, newValue)) { onChange() }
}
}
Now use it anywhere you want and your infix function will be available:
var state: State by observableState(START) {
// property, oldValue, and newValue are all here on this object!
when {
START into STOP -> { // works!
doSomeMagic(newValue) // example accessing the newValue
}
}
}
Note a slightly different syntax than you are using for passing in the lambda function to the observableState function, this is more idiomatic to not declare the full function header and instead just have the lambda body with everything inferred. Now there are no parameters anyway.
The cost of this is the new allocation of the small data class each time an event is fired.
Idea: Make an inlined convenience function to synchronize a function call on the "inlining" object's intrinsic lock:
public inline fun <T> intrinsicSync(block: () -> T): T {
return synchronized(intrinsicLockOfTheObjectUsingTheFunction) {
block()
}
}
fun someFunction(parameter: SomeClass) {
intrinsicSync(sharedResource.operation(parameter))
}
Is this possible? If not, is there a different but equally convenient way? I concede that
fun someFunction(parameter: SomeClass) {
synchronized(this) {
sharedResource.operation(parameter)
}
}
isn't the biggest hassle in the world, but I'd still like to know if it's possible to do something similar to what I imagined.
The most you can do is give the function a receiver (i.e. make it an extension function):
public inline fun <T> Any.intrinsicSync(crossinline block: () -> T): T {
// ^ within this function, `this` refers to the receiver
return synchronized(this, block)
}
and then in a member function, you can do
this.intrinsicSync { sharedResource.operation() }
and since this is inferred you should be able to shorten this to
intrinsicSync { sharedResource.operation() }
Keep in mind that extension functions cannot shadow member functions, so if this object has another function with the same signature, it won't choose the extension function.
In your case, though, this seems unlikely.