On Kotlin receiver object and extensions - kotlin

I'm trying to get familiar with the more advanced kotlin features, but i don't know why my little code example compiles and prints "Test A" while not printing "Test B".
Can anybody tell me why Test B is not printed?
class One{
fun delegate(operation: One.()-> Unit){
operation()
}
fun main(args: Array<String>){
val one= One()
fun One.doIt(){
println("Test A")
}
one.delegate { doIt() } //prints Test A
one.delegate {
fun One.doItDifferently(){
println("Test B") //not printed
}
}
}

That's because you don't call the function .toItDifferently(), you only define it (it is a local function, i.e. a function defined inside another function). Modify your example to call the function:
one.delegate {
fun One.doItDifferently(){
println("Test B")
}
doItDifferently() // <- here
}
(UPD, based on the comment) You can call delegate with an anonymous function like this:
one.delegate( // note the parenthesis, not the curly braces
fun One.(){
println("Test B")
}
)

This declaration
one.delegate {
fun One.doItDifferently(){
println("Test B") //not printed
}
}
will just create an empty function with a nested function. doItDifferently() is just declared, but never invoked. Hence, println("Test B") will never be executed.

You're not calling the function.
That is a local extension function on the class One and you are just declaring it. It now exists and is usable in the current scope, but does nothing by itself.
You would need to call/invoke it for it to do anything, as with any normal method:
one.delegate {
fun One.doItDifferently(){
println("Test B")
}
doItDifferently()
}
IntelliJ will actually give you a warning that this function is never used.
Edit: as per your comment, you aren't passing the function at all. This is not possible yet if you want to return the function as reflection does not support local functions.
If you try this:
class One {
fun delegate(operation: One.() -> KFunction<*>) {
operation().call(this)
}
}
fun main(args: Array) {
val one = One()
fun One.doIt() {
println("Test A")
}
one.delegate {
fun One.doItDifferently() {
println("Test B") //not printed
}
this::doItDifferently
}
}
You'll get:
Exception in thread "main" kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Introspecting local functions, lambdas and anonymous functions is not yet fully supported in Kotlin reflection

Related

Kotlin inline function only have a return statement

I want to use inline function return outside function(skip print the "after"). So when I use
inline fun test() {
return
}
fun test1() {
println("before")
test()
println("after")
}
test1()
the output is
before
after
When I use
inline fun test(callBack: () -> Unit) {
callBack()
}
fun test1() {
println("before")
test {
return
}
println("after")
}
test1()
the output is
before
So I want to know why the return statement in the first inline function is not working. Why does the first block of code not work?
As per documentation:
(return) by default returns from the nearest enclosing function or anonymous function. -- returns-and-jumps
A return statement without a label always returns from the function declared with the fun keyword. -- anonymous-functions
Actually I would revert the question: why does it work in the second example? And the answer is:
In Kotlin, we can only use a normal, unqualified return to exit a named function or an anonymous function. This means that to exit a lambda, we have to use a label, and a bare return is forbidden inside a lambda, because a lambda cannot make the enclosing function return (...). But if the function the lambda is passed to is inlined, the return can be inlined as well, so it is allowed.
-- non local returns
you can use kotlin Crossinline
Crossinline allows you to mark a lambda to forbid it from using non-local returns. If you remember, inline lambdas interprets return as non-local returns and return is not allowed on non-inlined lambdas
fun main(args: Array<String>) {
test1()
}
inline fun test(crossinline callBack: () -> Unit) {
callBack()
}
fun test1() {
println("before")
test {
return#test
}
println("after")
}
then the output will be has expectes

Kotlin - Trying to factorize code with high-order function

I'm quite new to Kotlin and I'd like to see if using high-order functions can help in my case.
My use-case is that I need to call the methods of an IInterface derived class to send events to one or more components. And I'd like to make this generic, and I want to check if a high-order funtion can help. A sample of code will help to understand (well, I hope so!).
private val eventListeners = mutableListOf<IEventInterface>() // List filled somewhere else!
private fun sendConnectionEvent(dummyString: String) {
val deadListeners = mutableListOf<IEventInterface>()
eventListeners.forEach {
try {
it.onConnectionEvent(dummyString)
} catch (e: DeadObjectException) {
Log.d(TAG, "Removing listener - Exception ${e.message}")
deadListeners.add(it)
}
}
deadListeners.forEach { it ->
eventListeners.remove(it)
}
}
private fun sendWonderfulEvent(dummyString: String, dummyInt: Int) {
val deadListeners = mutableListOf<IEventInterface>()
eventListeners.forEach {
try {
it.onWonderfulEvent(dummyString, dummyInt)
} catch (e: DeadObjectException) {
Log.d(TAG, "Removing listener - Exception ${e.message}")
deadListeners.add(it)
}
}
deadListeners.forEach { it ->
eventListeners.remove(it)
}
}
I added 2 similar methods (I will have many more in the real use case) and I think (I hope!) that something could be done but I can't make high-order function works in this case because:
I want to call the same method on several instances, and not 'just' a basic function
To make things even worse, the methods I need to call don't have the same prototype (that would have been too easy!).
Hope this is clear enough.
Thanks for your help!
VR
Here is how it can be done
fun onEvent(body: (IEventInterface) -> Unit) {
val deadListeners = mutableListOf<IEventInterface>()
eventListeners.forEach {
try {
body(it)
} catch (ex: DeadObjectException) {
Log.d(TAG, "Removing listener - Exception ${e.message}")
deadListeners.add(it)
}
}
deadListeners.forEach { it ->
eventListeners.remove(it)
}
}
Supposing an interface like this:
interface IEventInterface {
fun onConnectionEvent(dummyString: String)
fun onWonderfulEvent(dummyString: String, dummyInt: Int)
}
Define an generic type that implements your defined interface ( <T : IEventInterface>)
Define an mutable list of this type to receive your implementation (MutableList<T>.removeIfThrows)
Expect an extension function for you type that will do your specific validation (and custom parameters if you want)
Using an apply and returning the instance you can run your code like a pipeline
Executing the custom validation when you want
private fun <T : IEventInterface> MutableList<T>.removeIfThrows(validation: T.() -> Unit, customLogMessage: String? = null): MutableList<T> {
return apply {
removeIf {
it.runCatching {
validation()
}.onFailure { error ->
print(customLogMessage ?: "Removing listener - Exception ${error.message}")
}.isFailure
}
}
}
Define your specific implementation passing just the function with custom validation as an parameter
private fun <T : IEventInterface> MutableList<T>.sendConnectionEvent(dummyString: String) = removeIfThrows({
onConnectionEvent(dummyString)
})
private fun <T : IEventInterface> MutableList<T>.sendWonderfulEvent(dummyString: String, dummyInt: Int) = removeIfThrows({
onWonderfulEvent(dummyString, dummyInt)
})
Now you can run your code like an pipeline modifying your original object like this
private fun nowYouCanDoSomethingLikeThis() {
eventListeners
.sendConnectionEvent("some dummy string")
.sendWonderfulEvent("some another dummy string", 123)
}

How can I print a message using a lambda in Kotlin

I want to print a() message using lambda, but I can't do this.
printed Nothing in console. How can I do it?
var quackBehavior: QuackBehavior? = null
fun a(){
quackBehavior?.let{
it("I want print this message")
}
}
fun printMessage(){
quackBehavior = {
println(it)
}
}
fun main() {
a()
}
typealias QuackBehavior = (String) -> Unit
``
In your code you don't call printMessage function, so quackBehavior remains null. If you add printMessage() call in main function before a() call, it will work as intended.
quackBehavior?.let {
it("I want print this message")
}
The lambda you passed to let will only be invoked if quackBehaviour != null. This is the case because you use the safe call operator ?. which will only invoke functions on a non-null receiver.
Since you don't call printMessage, where you intialize quackBehaviour it remains null.
So to fix it, invoke your functions like this:
fun main() {
printMessage()
a()
}
Optional:
You could simplify your code like this:
fun main() {
var quackBehavior: (String) -> Unit = {
println(it)
}
quackBehavior("I want print this message")
}

Single-function listeners using lambda

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.

How can you call different versions of similar extension methods with kotlin

I have the following functions to simulate the ternary operator for kotlin
fun Boolean.then(action: () -> Unit): Boolean {
if (this)
action.invoke()
return this
}
fun Boolean.otherwise(action: () -> Unit) {
if (!this)
action.invoke()
}
fun <T> Boolean.then(func: () -> T): T? {
if (this)
return func.invoke()
return null
}
fun <T> T?.otherwise(action: () -> T): T {
return this ?: action.invoke()
}
they are supposed to be used like this :
(check).then { doHello() }.otherwise { doWorld() }
val answer = (check).then { "hello" }.otherwise { "world" }
however when I try to assign a value using the above operators like this:
val visibility: Int = (show).then { View.VISIBLE }.alt { View.GONE }
I get an error saying that the required reply was Int but it actually got Unit which means that it called the first version of the methods instead of the second
Other than renaming the methods (when I changed the first two to thenDo and otherwiseDo it worked), can I write the above code in some way so that the compiler will know to call the second version?
I don't think you need both overloads. If you remove the ones that return Unit, then both your lines of code work:
(check).then { doHello() }.otherwise { doWorld() }
val answer = (check).then { "hello" }.otherwise { "world" }
That's because the first line, where the lambdas return Unit, e.g. doHello(), can still use the generic versions of then and otherwise, as they are still considered functions with a return value, namely Unit.
Although I agree with some the comments above: do you really need this? Why not just use if, which is an expression which returns a value (like the ternary operator). See discussion here for more info.