I'm learning Kotlin and I have some trouble with functions.
I'm trying to create something like a functional interface with a generic parameter.
In Java I would create something like this:
#FunctionalInterface
public interface Foo<T extends Bar> {
String something(T arg);
}
Then I can use this somewhere else like this (given that Person extends Bar:
Foo<Person> f = p -> p.toString();
How do you write this with Kotlin?
The first thing I tried was to use type-aliases like this:
typealias Foo<T> = (T) -> String
However, it stopped working when I added the bound to the type parameter:
typealias Foo<T: Bar> = (T) -> String // Error: Bounds are not allowed on type alias parameters
The second approach was to write an interface that extends the function type:
interface Foo<T: Bar> : (T) -> String
However, now I don't know how to instantiate a lambda function from with this. It works when I create class from it like this:
class Something: Foo<Person> {
override fun invoke(p: Person): String {
return p.toString()
}
}
val f = Something()
But this is a big overhead and I'm sure there has to be a better solution.
So how can I define a function signature that can be reused by many functions that supports generic parameters with bounds in kotlin?
Most of the time (always?) it is sufficient to define the type of the lambda in the parameter of the function that receives it.
For example:
open class Bar
class Person: Bar()
var f = { p: Person -> p.toString() }
fun <T : Bar> withFoo(block: (T) -> String) { }
fun <T : Bar> otherFoo(block: (T) -> String) { }
fun main() {
withFoo(f)
otherFoo(f)
}
The same way the Kotlin documentation states:
"since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported."
See https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
Related
I want to make a class extend multiple function type interfaces.
This works since the function types have different signatures, () -> Unit and (String) - Unit
typealias A = () -> Unit
typealias B = (something: String) -> Unit
class Test : A, B {
override fun invoke() {
TODO("Not yet implemented")
}
override fun invoke(something: String) {
TODO("Not yet implemented")
}
}
Now if I add a third function type, the compiler complains about Conflicting Overloads or A supertype appears twice
typealias A = () -> Unit
typealias B = (something: String) -> Unit
typealias C = (somethingElse: String) -> Unit
class Test : A, B, C {
override fun invoke() {
TODO("Not yet implemented")
}
override fun invoke(something: String) {
TODO("Not yet implemented")
}
override fun invoke(somethingElse: String) {
TODO("Not yet implemented")
}
}
I can obviously go and add garbage params to C to make it work, but this seems more like a hack
typealias C = (somethingElse: String, garbage: Unit?) -> Unit
but now if I define type D with the same signature,
typealias D = (somethingElseElse: String, garbage: Unit?) -> Unit
I would run into the same issue.
I thought that maybe value classes could help here:
#JvmInline
value class BString(val value: String)
#JvmInline
value class CString(val value: String)
typealias A = () -> Unit
typealias B = (something: BString) -> Unit
typealias C = (somethingElse: CString) -> Unit
class Test : A, B, C {
override fun invoke() {
TODO("Not yet implemented")
}
override fun invoke(something: BString) {
TODO("Not yet implemented")
}
override fun invoke(somethingElse: CString) {
TODO("Not yet implemented")
}
}
... but since value classes are compiled out of existence, that too is not a solution
Platform declaration clash: The following declarations have the same
JVM signature (invoke(Ljava/lang/Object;)Ljava/lang/Object;):
I'm assuming Kotlin KEEP 302, Binary Signature Name (https://github.com/Kotlin/KEEP/blob/binary-signature/proposals/multiplatform/binary-signature.md), would solve this issue in the future, but what is the correct way in the meantime to implement multiple function interfaces with the same signatures?
Practical use-case that I can think of: let's say you want to have a class that can handle Clickable and DoubleClickable, both would have something like (Event) -> Unit
EDIT: based on #mateusz's answer, this works, but only when using value classes, not if interface B and C are using normal Strings:
#JvmInline
value class BString(val value: String)
#JvmInline
value class CString(val value: String)
interface A {
operator fun invoke()
}
interface B {
operator fun invoke(something: BString)
}
interface C {
operator fun invoke(somethingElse: CString)
}
class Test : A, B, C {
override operator fun invoke() {
println("invoke A")
}
override operator fun invoke(something: BString) {
println("invoke B - something = $something")
}
override operator fun invoke(somethingElse: CString) {
println("invoke C - somethingElse = $somethingElse")
}
}
fun main(args: Array<String>) {
val handlerA = A::invoke
val handlerB = B::invoke
val handlerC = C::invoke
val t = Test()
handlerA(t)
handlerB(t, BString("hello B"))
handlerC(t, CString("hello C"))
}
outputs:
invoke A
invoke B - something = BString(value=hello B)
invoke C -somethingElse = CString(value=hello C)
The completer does not care about parameter's names.
The fun test(a: String): String and fun test(b: String): String are the same functions. When you will call test("some") then which function should be called?
You can create dedicated interfaces:
interface Clickable {
fun click(param: String)
}
interface DoubleClickable {
fun fastDoubleClick(param: String)
fun slowDoubleClick(param: String)
}
Then you can use function references if you want val handleClickFun: String -> Unit = Clickable::click
This will never work. At the fundamental JVM level, you can't implement the same interface twice with different generics. I would not expect this to ever work, even with the KEEP you mention.
Why do you want to extend function interfaces at all? If you just want the nice call syntax, you can have separate operator fun invoke overloads, without overriding anything. But even better would be using functions with actual names. If you need to pass it to methods accepting lambdas, use method references, e.g. Test::handleClick and Test::handleDoubleClick.
A typealias is just a way to give a convenient label to a specific type - it's not a type in itself, anywhere you specify that typealias, you can can just pass in a variable defined as the real type, or any other typealias you've derived from it.
So B and C are the same thing. You can have two different aliases for the same thing if that makes sense in different parts of your code (that's kinda the whole point of them! Relabel types to make them more readable or understandable) but that's just ways to refer to a type.
But when it comes to defining your class, it makes no sense. B and C are the same type, you're repeating yourself (and the compiler will give you a supertype appears twice error). And to implement that one type, you need one function - and only one, because if you have two identical functions then which one would get called?
So you can do this if you want:
typealias A = () -> Unit
typealias B = (something: String) -> Unit
typealias C = (somethingElse: String) -> Unit
class Test : A, B {
override fun invoke() {
println("invoke")
}
override fun invoke(something: String) {
println("invoke: $something")
}
}
fun doAThing(thing: C) {
thing("wow")
}
fun main() {
doAThing(Test())
}
doAThing takes a C, so we can pass it a B, because B is C.
I'm guessing that's not very useful to you, but that's the limitation of typealiases, and bare function types in general. If you want two separate functions with the exact same signature in the same scope, you need to be able to refer to them explicitly - and that usually means giving them different names.
How is your click-handler class going to handle your Event if you can't tell it whether it's a single or double-click? And even if you could (e.g. through something like (handlerFunction as B).invoke(event)) then which of your identical overridden functions in the class is which?
Like Mateusz says, you need to use interfaces, and then you can pass references to the functions, because you have a name for each one you can refer to. The things you're passing those functions into can define the types using typealiases if they want. And if you want a type that can handle both kinds of clicks, create another interface that implements both types.
If you want to be able to pass a single object that has multiple functions with the same signature, that's what you need. If you want to use function types instead, you'll have to pass the individual function references in - but something somewhere has to be able to distinguish between them in the first place, and that's usually where they're defined
I wanted to be able to define a method to clone an object that is the same type of itself. I define the interface requesting such, but the following does not compile or run.
interface Foo {
fun <T: Foo> copy() : T
}
class Bar(private val v:Int) : Foo {
override fun copy():Bar = Bar(v)
}
main() {
val bar1 = Bar(1)
val bar2 = bar1.copy()
}
If however I write the implementing class in Java, it will compile
class Bar implements Foo {
private int v;
public Bar(int v) {this.v = v;}
public Bar copy() {
return new Bar(v);
}
}
I can rewrite the code like the following that compiles:
interface Foo<out Foo>{
fun copy(): Foo
}
class Bar(private val v:Int) : Foo<Bar> {
override fun copy(): Bar = Bar(v)
}
However the following will fail with error: no type arguments expected for fun copy(): Foo
val newF = f.copy()
fun <T: Foo> addFoo(
foo: T,
fooList: List<T>,
): MutableList<T> {
val result: MutableList<T> = arrayListOf()
for (f in fooList) {
val newF = f.copy<T>()
result.add(newF)
}
result.add(foo)
return result
}
Is there a good solution to the problem?
The problem here is that Foo doesn't know the exact type of the implementing class, so has no way to specify that its method returns that same type.
Unfortunately, Kotlin doesn't have self types (see this discussion), as they would handle this situation perfectly.
However, you can get close enough by using what C++ calls the curiously-recurring template pattern. In Kotlin (and Java) you do this by defining Foo with a type parameter explicitly extending itself (including its own type parameter):
interface Foo<T : Foo<T>> {
fun copy(): T
}
Then the implementing class can specify itself as the type argument:
class Bar(private val v: Int) : Foo<Bar> {
override fun copy(): Bar = Bar(v)
}
And because T is now the correct type, everything else works out. (In fact, the : Bar is redundant there, because it already knows what the type must be.)
Your addFoo() method will then compile with only a couple of changes: give it the same type parameter <T: Foo<T>>, and remove the (now wrong, but unnecessary) type parameter when calling f.copy(). A quick test suggests it does exactly what you want (creates a list with clones of fooList followed by foo).
Since it's often useful for a superclass or interface to refer to the implementing class, this pattern crops up quite often.
BTW, your code is easier to test if Bar has its own toString() implementation, as you can then simply print the returned list. You could make it a data class, or you could write your own, e.g.:
override fun toString() = "Bar($v)"
Code fragment in Kotlin
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> {
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(receiver, probeCompletion)
else {
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
}
}
}
What I want to know is How (suspend R.() -> T) AKA Function Type can be recognized
as BaseContinuationImpl which is a Classify Type even to cast as `Function2'.
Any help will be appreciated.
There are no separate function types and class types. Function types are just types that can be executed with specific arguments and specific return type. They are interchangeable with Function0, Function1, etc. interfaces and they contain a single invoke() function.
We can implement a function type by our class:
class MyClass : (suspend () -> Unit) {
override suspend fun invoke() {}
}
Now, let's get this code:
val lambda: (suspend () -> Unit) = {}
After disassembling we see that our lambda is compiled to:
final class FooKt$foo$lambda$1 extends kotlin/coroutines/jvm/internal/SuspendLambda implements kotlin/jvm/functions/Function1 {
...
public final invoke(Ljava/lang/Object;)Ljava/lang/Object;
...
}
It extends SuspendLambda which is a subtype of BaseContinuationImpl. It also implements Function1 and contains invoke function which makes it a function type.
I'm trying to write serialization functions to be able to serialize any vector (=ArrayList) in Kotlin, as well as primitive types and classes extending a Serialize class having a toBinary() function.
I also have a custom WriteDataStream class (code below) to serialize fields with the right format, endianness, etc.
I'm new to Kotlin but have experience in C++. In C++, I used templates and template specialization to solve that problem easily, but with Kotlin I've been struggling for a few days, without success.
I have a custom vector class MyVector which extends ArrayList and adds a maximum size. I want to serialize it with any generic type T, including inner vectors like a MyVector<MyVector<MyClass>>.
My WriteDataStream contains the following:
inline fun <reified T> write(vector: MyVector<T>) {
this.writeSize(vector.size.toULong(), vector.MAX_SIZE)
for (element in vector) {
write<T>(element)
}
}
inline fun <reified T: Serialize> write(value: T) {
writeSerialize(value as Serialize)
}
inline fun <reified T> write(value: T) {
when (T::class) {
UByte::class -> {
writeUInt8(value as UByte)
}
UShort::class -> {
writeUInt16(value as UShort)
}
UInt::class -> {
writeUInt32(value as UInt)
}
ULong::class -> {
writeUInt64(value as ULong)
}
Byte::class -> {
writeInt8(value as Byte)
}
Short::class -> {
writeInt16(value as Short)
}
Int::class -> {
writeInt32(value as Int)
}
Long::class -> {
writeInt64(value as Long)
}
Boolean::class -> {
writeBoolean(value as Boolean)
}
Float::class -> {
writeFloat(value as Float)
}
Double::class -> {
writeDouble(value as Double)
}
else -> {
error("Default serialization:" + T::class.qualifiedName)
}
}
}
All the underlying functions writeXXX() are tested and work fine. However, when tying to serialize a MyVector with a class extending Serialize, I fall in the "Default serialization" case:
#Test
fun writeVectorOfStructure() {
class TestStructure: Serialize() {
override fun toBinary(stream: WriteDataStream) {
stream.writeUInt32(17U)
stream.writeUInt8(3U)
stream.writeDouble(555.555)
}
}
val value = MyVector<TestStructure>(MAX_SIZE, arrayListOf(TestStructure(), TestStructure()))
writeStream.write(value)
val bytes: UByteArray = writeStream.byteArray()
Assert.assertEquals(bytes.size, 28) // = 2 (for size) + 2*(4+1+8) = 28 bytes
}
So my question is: Why does Kotlin not use the function
inline fun <reified T: Serialize> write(value: T)
when it serializes an element of the vector (write<T>(element)) with generic T = Serialize, but instead uses the more generic one?
inline fun <reified T> write(value: T)
In C++, the compiler always uses the most fitted function.
Is there a way to overcome this limitation in Kotlin?
I have tried with and without reified types, I have tried a non-generic function as well: inline fun write(value: Serialize), but without success. The only thing that seems to work was to add a case for classes "instance of" Serialize in the fully-generic inline fun <reified T> write(value: T), but this is not really a nice solution.
Thanks you !
JVM and its bad implementation of generics
You are a victim of Java's implementation of generics, more specifically the erasure. C++ uses what is called type expansion to implement generics, meaning if you declare MyType<A> and MyType<B>, at runtime you will have two different types, language runtime will create them for you.
On the other hand what Java does is called the erasure implementation. so in java world when you say List<String> and List<Integer>, at runtime they are both identical types, that is system doesn't have any information to make a distinction between both of these, they are List type (Note that there is no type parameter, it got removed during the compilation).
Lets decompile your code and see for yourself
I wrote following code in kotlin, it matches yours
class SomeType {
inline fun <reified T: String> write(value: T) {}
inline fun <reified T> write(value: T) {}
inline fun <reified T: Any> write(vector: List<T>) {
for (element in vector) {
write(element)
}
}
}
And when I decompile the code it gives me following. (Only relevant code included)
public final class SomeType {
public final void write(#NotNull String value) {}
public final void write(Object value) {}
public final void write(#NotNull List vector) {
boolean var6;
for(Iterator var4 = vector.iterator(); var4.hasNext(); var6 = false) {
Object element = var4.next();
}
}
}
Look at the write(vector: List<T>) method's decompilation. parameter type got changed to List which is a Raw Type and its components are objects.
And for an Object best method match is public final void write(Object value) and not the one with String or in your case Serialize.
I want to implement a functional kotlin interface (interface with a single abstract method) as a kotlin lambda. How to do that?
Kotlin interface
#FunctionalInterface
interface Foo{
fun bar(input: String): String
}
Kotlin implementation .
fun createFoo(): Foo {
return { input: String -> "Hello $input!" }
}
↑ doesn't compile ↑
It has to be implemented as object, which is ugly as hell.
fun createFoo() =
object : Foo{
override fun bar(input: String)= "Hello $input"
}
EDIT: corrected my sample interface from java to kotlin
Just add the fun keyword to your interface declaration:
fun interface Foo {
fun bar(input: String): String
}
It is the notation of functional interfaces in Kotlin (instead of #FunctionalInterface annotation in Java).
Now you can implement it like this:
Foo { input: String -> "Hello $input!" }
See more: https://kotlinlang.org/docs/fun-interfaces.html
since Kotlin v1.4
SAM conversion will be supported with version 1.4, with a new type inference algorithm.
See:
What to Expect in Kotlin 1.4 and Beyond
More powerful type inference algorithm
before Kotlin v1.4
It works if the companion object implements the invoke function taking a lambda.
Kotlin interface
interface Foo{
fun bar(input: String): String
companion object {
inline operator fun invoke(crossinline function: (String) -> String) =
object : Foo{
override fun bar(input: String) = function(input)
}
}
}
Kotlin implementation
fun createFoo()= Foo { input: String -> "Hello $input!" }
Functional/SAM interfaces defined in kotlin can't be implemented as Kotlin lambdas by design, see KT-7770.
In Kotlin an functional / SAM interface is considered as an anti-pattern, a function type should be declared instead: (String)->String. The function type can be expressed as typealias to make it look and feel like an interface: typealias Foo=(String)->String.
Note: The typealias is not visible in Java code only in Kotlin!
I want to implement a functional kotlin interface (interface with a single abstract method) as a kotlin lambda. How to do that?
Can't
It has to be implemented as object, which is ugly as hell.
Indeed.
You have two options:
1.) use typealias
typealias Foo = (String) -> String
fun createFoo(): Foo = { "Hello $it!" }
2.) depending on your API, you can define an extension function that receives the functional type (String) -> String as a crossinline argument, then invokes it inside a object: __ block. That way, you can hide the object: in a given function, and externally still be able to call it with a lambda argument. Doesn't seem applicable in this case, though.
I don't think there is a language level option to do this, but you can abstract the "ugly" code into a helper method so its easier to read where the business logic is actually needed:
Helper Method
fun Foo(body: (String) -> String) = object : Foo{
override fun bar(input: String)= body(input)
}
Business Code
fun createFoo():Foo {
return Foo {input:String -> "Hello $input!"}
}
It would be easier in your case to have the interface in Java:
fun createFoo() : Foo = Foo { "hello $it" }
But as you have a Kotlin interface instead, you are a bit out of luck here. Here is a related issue regarding this: KT-7770
A workaround to this could be (but that mainly depends on how you use that interface) to have a Kotlin interface as follows in place that is the main entry point for the Java side:
interface Foo : (String) -> String
On the Kotlin side you will not use it and the Java side should only use it to deliver functions, e.g.
// Java class
public class JFooFactory implements FooFactory {
#Override
#NotNull
public Foo createFoo() { // uses the Foo-interface from Kotlin
return input -> "hello " + input;
}
}
// Kotlin equivalent:
class KFactory : FooFactory {
override fun createFoo() : (String) -> String = {
"hello $it"
}
}
where the corresponding FooFactory-interface could look like:
interface FooFactory {
fun createFoo() : (String) -> String
}
Usage could look like:
listOf(KFooFactory(), JFooFactory())
.map {
it.createFoo()
}
.forEach { func : (String) -> String -> // i.e. func is of (sub)type (String) -> String
func("demo") // calling it to deliver "hello demo" twice
}
Alternatively, to have also that Foo-feeling for Kotlin you can do it as follows:
typealias Foo = (String) -> String
interface JFoo : Foo
// or if you put the interface in its own package you could also use:
interface Foo : someother.package.Foo
then the Java code stays the same as above, either with JFoo or with Foo pointing to that other package; the typealias is not visible from Java. The Kotlin side would change to the following:
class KFactory : FooFactory {
override fun createFoo() : Foo = {
"hello $it"
}
}
The Factory-interface could also be replaced:
interface FooFactory {
fun createFoo() : Foo
}
Under the hood however everything stays the same. We have/use (String) -> String in Kotlin and Foo-functional-interface in Java.
To use Kotlin lambda to Java SAM interface conversion anywhere you want, simply specify the name of the interface before the lambda.
fun createFoo(): Foo {
return Foo { input:String -> "Hello $input!" }
}
It doesn't even need to be that long.
fun createFoo(): Foo {
return Foo { "Hello $it!" }
}
As long as the interface has a single method and is declared in Java, that's all you need to do.