Kotlin customer interface declaration - android-recyclerview

I'm having some issues converting my existing Java code to Kotlin, specifically with a custom click listener that I used with a recycle view. Here's what I got so far.
//this is the click listener interface (in Kotlin)
interface RecyclerClickListener {
fun onClick(view: View, position: Int)
}
In Java this is how I would create and use the interface
RecyclerClickListener clickListener = (view1, position) -> {
setSelectedDate(dateCards.get(position).getDateTime());
DateCardAdapter adapter = (DateCardAdapter) date_recycler_view.getAdapter();
adapter.setSelected(position);
};
DateCardAdapter cardAdapter = new DateCardAdapter(dateCards, getActivity(), clickListener, true );
Now this is how I'm trying to do it in Kotlin (most of this was auto-converted in Android Studio)
val listener: RecyclerClickListener = { view1: View, position: Int ->
setSelectedDate(dateCards[position].dateTime)
val adapter = sun_date_recycler_view.adapter as DateCardAdapter
adapter.setSelected(position)
} as RecyclerClickListener
val cardAdapter = DateCardAdapter(dateCards, activity!!, listener, true)
But when I launch my app I keep getting a ClassException when trying to create the listener
Caused by: java.lang.ClassCastException: .fragments.SunFragment$onViewCreated$listener$1 cannot be cast to .interfaces.RecyclerClickListener

The lambda you're declaring is of type (View, Int) -> Unit, which is not a subtype of RecyclerClickListener. Kotlin has a feature called SAM conversion that allows you to create anonymous instances of functional (single method) interfaces with lambdas, but this only works for interfaces defined in Java.
(...) note that this feature works only for Java interop; since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported.
This is your first (and possibly best, for now) solution - define the interface in Java, and you'll be able to use lambdas in both languages with no problem.
Another one is to use a functional type as the Kotlin docs suggest, this would require you to use the type (View, Int) -> Unit instead of defining a new interface. This way, both your lambda in Kotlin and Java would work as they are (without casts). You can use a typealias to refer to it by a name instead of the functional type in Kotlin code, but this won't be visibile in Java:
typealias RecyclerClickListener = (String, Int) -> Unit
You'll also have to expliclty return Unit.INSTANCE at the end of any Java lambdas:
(view, position) -> {
System.out.println("Clicked");
return Unit.INSTANCE;
}
You can find discussion about SAM conversion possibly being supported in the future for Kotlin interfaces as well here.

If you use IntelliJ to convert a Java class defining such a listener defined as an anonymous inner class to Kotlin, it will generate the following, correct code:
val listener = object : RecyclerClickListener {
override fun onClick(view: View, position: Int) {
// ...
}
}
The syntax is, BTW, described in the Kotlin documentation: https://kotlinlang.org/docs/reference/nested-classes.html#anonymous-inner-classes

Related

Extension function from a generic interface

Consider the following interface
interface EntityConverter<in A, out B> {
fun A.convert(): B
fun List<A>.convert(): List<B> = this.map { it.convert() }
}
I want to use it in a spring boot application where specific implementations get injected so that the extension function becomes usable on the type.
However this doesn't work. The compiler does not resolve the extension function.
Note that you're defining extension functions that are also member functions of the EntityConverter type. You should take a look at this part of the doc for information about how this works.
Essentially, in order to use them, you need 2 instances in scope:
the dispatch receiver (an instance of EntityConverter<A, B>)
the extension receiver (an instance of A or List<A>, where A matches the first type parameter of the EntityConverter in scope)
You can use with() to bring the EntityConverter in scope so you can use convert on your other instances using the usual . syntax:
val converter = object : EntityConverter<Int, String> {
override fun Int.convert() = "#$this"
}
val list = listOf(1, 2, 3)
val convertedList = with(converter) {
list.convert()
}
println(convertedList) // prints [#1, #2, #3]
Now you have to decide whether this kind of usage pattern is what makes most sense for your use case. If you'd prefer more "classic" calls without extensions (converter.convert(a) returning a B), you can declare your functions as regular methods taking an argument instead of a receiver.
Bonus: functional interface
As a side note, if you add the fun keyword in front of your EntityConverter interface, you can create instances of it very easily like this:
val converter = EntityConverter<Int, String> { "#$this" }
This is because your converter interface only has a single abstract method, making it easy to implement with a single lambda. See the docs about functional interfaces.
I'm not sure if you can mention extension functions as a part of interface, because it's like static functions.
I'd recommend to put "common" function in interface with A typed parameter. Then just put extension method for list nearby.
interface EntityConverter<in A, out B> {
fun convert(a: A): B
}
fun <A, B> EntityConverter<A, B>.convert(list: List<A>): List<B> = list.map { convert(it) }
Update
I wasn't aware about possibility of inheritance of extension methods in Kotlin. And about its overriding as well. So my answer could be just an alternative of using extension methods.

Kotlin - Functional (SAM) interfaces VS Function types

With Kotlin 1.4 we now have Functional Interfaces
fun interface Todo {
fun run()
}
fun runBlock(todo: Todo){
if(condition)
todo.run()
}
fun runBlock{
println("Hello world")
}
Before i was always using (T) -> T
inline fun runBlock(block: ()-> Unit){
if(condition)
block()
}
fun runBlock{
println("Hello world")
}
So basically I can make the same task with both methods , there is any performance advantage by using Functional SAM() Interfaces over Function Type?.
It's a performance dis-advantage because the lambda is no longer inlined (unless the JIT decides to, but it won't be instant). Even if you mark runBlock as inline, the compiler will warn you that the argument won't be inlined.
There are only two reasons to use fun interfaces instead of function types:
Backwards compatibility when porting code using Java functional interfaces.
Not exposing Kotlin function types in API intended for use from Java.
To expand on point 1: before Kotlin 1.4 it was advised to keep functional interfaces as Java code, even if all your other code was Kotlin. This way you could use lambdas for parameters of those types both in Java and Kotlin code. But if you declared the interface in Kotlin, you could only use lambdas for them in Java.
https://kotlinlang.org/docs/reference/whatsnew14.html#sam-conversions-for-kotlin-interfaces
the compiler automatically converts the lambda to an instance of the class that implements the abstract member function.
So, no performance advantage, it’s the same thing as before. The compiler now does what you had to do before.
As other answers and comments have pointed out, in your case, using inlined lambda is faster, since there is no function call overhead when invoking it.
However, there is one specific use case where using SAM interface is faster, that is when you 1. do not inline the lambda and 2. the arguments/return value of the lambda is a primitive (or any other type that may cause boxing when used with generics).
For example, using SAM interface like so:
fun interface Foo() {
fun run(i: Int): Int
}
fun foo(fn: Foo) {
fn.run(42)
}
foo { it * 2 }
Will not cause any boxing when invoked, while:
fun foo(fn: (Int) -> Int) {
fn(42)
}
foo { it * 2 }
Will box the integer argument since (Int) -> Int is essentially Function1<Integer, Integer> in Java, which uses generic.

How do I create a lambda expression from a Kotlin interface?

I have a simple Kotlin interface:
#FunctionalInterface
interface ServiceMethod<T> {
fun doService(): T
}
This, in spite of the name, is essentially identical to Java's Supplier functional interface. The only difference is that I can implement the Supplier, and I can't implement my own.
val supplier = Supplier<String> {
"Hello"
}
val serviceMethod = ServiceMethod<String> {
"Hello"
}
The ServiceMethod implementation gives me a compiler error saying "Interface ServiceMethod does not have constructors." Huh? Of course it doesn't! It's a functional interface.
I know that I can write it as an anonymous inner class:
val serviceMethod = object : ServiceMethod<String> {
override fun doService(): String {
return "Hello"
}
}
But this is much more verbose. In this case I could just use the Supplier interface, but that won't work for other interfaces. I shouldn't have to write an interface in Java, just to be able to a lambda in Kotlin. I'd rather use a lambda for all my Kotlin interfaces, especially since I'll be writing a lot of these. Am I missing something obvious?
Use the fun interface modifier since Kotlin 1.4
In Kotlin 1.3 and earlier, SAM (single abstract method) conversions, where you can instantiate an interface like Supplier using a lambda function, were only supported for Java interfaces.
The language designers originally thought SAM conversions wouldn't be useful for Kotlin interfaces, because a Kotlin function already has a type. For example, the type of your doService function can be written as () -> T. Instead of creating an object that implements an interface, you could simply write:
val serviceMethod: () -> String = { "Hello" }
Kotlin 1.4 adds SAM conversions for Kotlin interfaces, but it doesn't work out of the box for every interface. Instead, you have to apply the special fun modifier to a Kotlin interface to make it eligible for SAM conversion.
In your example, it would simply look like this:
fun interface ServiceMethod<T> {
fun doService(): T
}
With the modifier added, you can create an instance using a lambda exactly as you were hoping in your question.
val serviceMethod = ServiceMethod<String> { "Hello" }
You can learn more in the Kotlin documentation for functional interfaces.

Kotlin multiplatform support for Optional

I'm working with a Java API now converted into multiplatform Kotlin. It used to use java.lang.Optional as the return type of many calls. I understand this is not the idiomatic Kotlin-way (see discussion) but this is an existing API, Optional stays (also it isn't a bad choice for the Java-facing client). My question is how?
Note: The code only needs to return Optional.of(x) or return Optional.empty() to the external API. Any internal uses will be purged.
How do we use expect/actual/typealias to use the real Optional class when available?
Is there a way to avoid re-implementing a fake Optional class on non-Java targets (i.e. work idiomatically with nullable? suffix)
At this point, Kotlin doesn't allow providing an actual typealias for an expected class with a companion object by using a Java class with matching static declarations. Follow this issue for updates: KT-29882.
For now, you can workaround that by declaring the factory functions separately, outside the expected Optional class, as follows:
expect class Optional<T : Any> {
fun get(): T
fun isPresent(): Boolean
/* ... */
}
expect object Optionals {
fun <T : Any> of(t: T): Optional<T>
fun empty(): Optional<Nothing>
}
That should not necessarily be an object, you could just use top-level functions.
Then, on the JVM, you would have to provide an actual typealias for the Optional class and, additionally, provide the trivial actual implementation for the Optionals object:
actual typealias Optional<T> = java.util.Optional<T>
actual object Optionals {
actual fun <T : Any> of(t: T): Optional<T> = java.util.Optional.of(t)
actual fun empty(): Optional<Nothing> = java.util.Optional.empty()
}
As for not providing an implementation for the non-JVM platforms, I doubt it's possible, as that would require some non-trivial compile-time transformations of the Optional usages to just the nullable type. So you would want something like this:
actual typealias Optional<T> = T?
which is now an error:
Type alias expands to T?, which is not a class, an interface, or an object
So you actually need a non-JVM implementation. To avoid duplicating it for every non-JVM target, you can declare a custom source set and link it with the platform-specific source sets, so they get the implementation from there:
build.gradle.kts
kotlin {
/* targets declarations omitted */
sourceSets {
/* ... */
val nonJvmOptional by creating {
dependsOn(getByName("commonMain"))
}
configure(listOf(js(), linuxX64())) { // these are my two non-JVM targets
compilations["main"].defaultSourceSet.dependsOn(nonJvmOptional)
}
}
}
Then, inside this custom source set (e.g. in src/nonJvmOptional/kotlin/OptionalImpl.kt) you can provide an actual implementation for the non-JVM targets.
Here's a minimal project example on Github where I experimented with the above: h0tk3y/mpp-optional-demo

How to overload functions with same param types but one is nullable

I'm trying to add two extension function to Calendar Class in Android to convert the specific date-time pattern to Calendar and vise versa
fun Calendar.fromIsoString(date: String): Calendar = this.apply {
time = SimpleDateFormat(SERVER_DATE_PATTERN, Locale.US).parse(date)
}
fun Calendar.fromIsoString(date: String?): Calendar? {
if (date == null) return null
time = SimpleDateFormat(SERVER_DATE_PATTERN, Locale.US).parse(date)
return this
}
But it gives me the following Error:
Platform declaration clash: The following declarations have the same JVM signature (fromIsoString(Ljava/util/Calendar;Ljava/lang/String;)Ljava/util/Calendar;)
Is it possible to have these two functions besides each other? how?
Note:
I somehow handled the problem by adding a Unit optional parameter to one of the functions:
fun Calendar.fromIsoString(date: String?, a: Unit = Unit): Calendar? {
if (date == null) return null
time = SimpleDateFormat(SERVER_DATE_PATTERN, Locale.US).parse(date)
return this
}
But I think it is tricky and not a good practice!
Is there any better solution?
There is a simple solution that allows you to do this without changing your methods or the name you want to call them from Kotlin.
For Kotlin the distinction between the two methods is clear, but for the JVM it is not. Therefore, just tell Kotlin that you want a different internal JVM name for one of the methods which will not impact Kotlin code at all. Only Java code would see the alternative name. For example:
fun Calendar.fromIsoString(date: String): Calendar = this.apply {
// ... your code without change
}
#JvmName("fromIsoStringNullable") // <-- this solves your problem without changing the name in Kotlin
fun Calendar.fromIsoString(date: String?): Calendar? {
// ... your code without change
}
voilà! no more error! You can call either version from Kotlin using the same name someCalendar.fromIsoString(...)
Other answers and comments suggesting the nullability is syntactical sugar are far from correct. These types are written into the bytecode as extra metadata that only Kotlin will see and enforce. But on top of that, it also adds a #Nullable annotation to the parameter. In fact, all Kotlin generated function parameters have #NotNull and #Nullable annotations in the bytecode for other languages or analyzers to see and enforce if they care enough to do so.