I came across this custom onClickListener that handles clicks(in one of Udacity courses), I come from java world so even that I know what this does, I don’t understand this syntax very well. I know that is getting a function as parameter( a lambda ) but how it gets the behavior of an interface I don’t know . Can someone help me understand ? (I am trying to convert it to java first and then understand it more why they did it this way)
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}
What do you mean by saying that “it gets the behavior of an interface”? Functions in Kotlin are first-class citizens, so you can pass functions as any other data objects and invoke them if needed.
You shouldn’t assume that you will be able to convert any possible Kotlin code 1:1 to Java, but in this case probably the best analogy would be to replace (marsProperty:MarsProperty) -> Unit with: Consumer<MarsProperty>:
public class OnClickListener {
private Consumer<MarsProperty> clickListener;
public OnClickListener(Consumer<MarsProperty> clickListener) {
this.clickListener = clickListener;
}
public void onClick(MarsProperty marsProperty) {
clickListener.accept(marsProperty);
}
}
I think I wasn’t clear enough(I know the concept of lambdas), what I meant is that in Java(Android) I always used interfaces to implement callbacks, and now when I saw a callback implemented without an interface I got confused, but I guess that this is just another way in Kotlin to implement callbacks right ?
Well, if you compile Kotlin to JVM bytecode, it really works in the very same way as in Java - it uses interfaces. I guess it can’t be different, because JVM does not understand anything else.
(marsProperty:MarsProperty) -> Unit is internally represented as Function1<MarsProperty, Unit>. Function1 is an interface with invoke() method. clickListener(marsProperty) line basically calls: clickListener.invoke(marsPropery).
Note that lambdas in Java work in exactly the same way. You provide lambda to some function, but internally it is converted to an implementation of some interface. This is sometimes called “SAM conversion”.
Hi @cylab , I saw the link and it shows a way to use a lambda to implement with less code an interface, but in the code I showed above there isn’t any Interface (there is just the class named OnClickListener like @broot showed above) declared like IntPredicate interface on the link you provided.
Actually, there is an interface in my above code. Consumer is an interface and it is the direct equivalent of the Kotlin’s lambda.
Also, if you are confused with your above code, then this is understandable, because this code is in fact confusing. Maybe someone created it to provide Java->Kotlin interoperability, but other than that it doesn’t make too much sense. I don’t see the point of creating this OnClickListener - it is just a wrapper around a function object, but it does not do anything more.
You are right, I probably should have added this from the start, here is the example I was following.
And here is the exact part of the course when this part is implemented. (*Note: this was used on some other part of the course but this is one of the cases I was looking)
Yeah, this is just a pointless wrapping of the lambda. In line 77 of
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
val marsProperty = getItem(position)
holder.itemView.setOnClickListener {
/* 77 */ onClickListener.onClick(marsProperty)
}
holder.bind(marsProperty)
}
the onClick() function of that OnClickListener is just called inside a SAM-conversion lambda that is passed to setOnClickListener.
Edit: To elaborate further, holder.itemView.setOnClickListener expects an interface, which is on-the-fly implemented in SAM-conversion manner by the curly braces. The single method of the interface contains the call to onClick() of that custom OnClickListener, that in turn forwards to the clickListener function passed in the constructor of OnClickListener.
It’s just wrapping of the Kotlin function inside an object, which frankly is just convoluted, misleading and confusing. They could just have opted to accept the original Interface in the PhotoGridAdapters constructor, or directly accept the clickListener function there instead.
Yes @broot, that is confusing , but now I saw this answer on stackoverflow and it says the following:
" This is most likely a hack from before Kotlin 1.4 to allow a pseudo-interface to be constructed with a lambda. Before Kotlin 1.4, you could only use [SAM conversion] with Java interfaces."
And also it says that:
And you can’t make some multi-purpose class implement the interface, because there is no interface.
And now reading the @cylab answer I think its becoming clear.
Thank you guys
They should criminalize this way of programming (pointless wrapping of the lambda)!
and also implementing clicklistener inside of onBindViewHolder? I’m insulted!
You provide lambda to some function, but internally it is converted to an implementation of some interface.
Not exactly, modern Java doesn’t implement an interface but takes advantage of invokeDynamic instruction to optimize the generated code. Check here for details.
Thanks for pointing this out. I must admit I don’t fully understand how invokedynamic works. Is it technically accurate to say that lambdas are still converted to interface implementations, but the process is two-step and it happens partially at compile time and partially at runtime?