Extending function in class

In fact, I didn’t even know that extending a functional type was available, but I found that it is available.
I wrote this code in common platform module:

class MyClass : () -> Unit

But it complained: ‘implementation function interface is prohibited in JavaScript’
Yes, I can understand why, but why this behavior is allowed in some platforms(like JVM)?

Also, are there any ways to enable this for all platforms?

1 Like

On JVM functional type literals are syntactic sugar for some interface name.

On JavaScript this seems not to be the case. I assume they are real function types on JS.

Seems like there are two parts to this question. 1: Why is implementing function types as interfaces allowed on some platforms, and 2: Why is it not allowed on all platforms?
.
Let’s start with the first part. Now, I am not part of the design team that came up with this feature so this is all speculation.
As you may know, Kotlin is first and foremost designed to be a modern drop-in replacement for Java with great Java interoperability. A lot of design choices mirror those of Java, not just for the sake of interoperability, but also in order to be familiar to the large number of developers who have a background in Java. Using interfaces to simulate function types has been a thing in Java, arguably even before Java 8 (Java 8 just added more convenient syntax and better support in the standard library). It’s also pretty much the only way to implement function types on the JVM. Thus, under the hood, Kotlin pretty much had to use interfaces anyway. The only question was whether to make this a feature in Kotlin language itself, or only keep it as an implementation detail. Personally, I cannot see any real drawbacks from including it (aside from JS compatibility, which probably was not a thing at this point in Kotlins history), and it does have it advantages (a way to define highly complex and potentially stateful functions in a way that takes better advantage of OOP)…

Now, for the second part. Kotlin/JS is not the primary target for Kotlin and this feature (function types as interfaces) was probably added with zero regard for JS compatibility. Instead, the question becomes: Can this feature work in Kotlin/JS without completely breaking JS compatibility? Unfortunately, the answer seems to be no. The problem is that in JS there is no invoke operator that can be defined on an arbitrary object and make it callable using reguler call syntax someFunctionObject(args). Thus, there is no way to compile a Kotlin class in a way that makes it callable from JS.

The fact that you can extend a functional type in kotlin (jvm) is an accidental result of the way function types work on the jvm (as Varia) explained. That said I don’t think I have ever seen any code doing this in kotlin and I can’t think of a good reason to use it over normal lambdas.
Compare

class MyCalback : (Data) -> Unit {
    override operator fun invoke (data: Data) {
        TODO()
    }
}

to

val myCallback = { data: Data ->
    TODO() 
}

Using the lambda syntax is much cleaner. I guess there are might be some special situation where this is not sufficent but I have never seen or heard about it. I guess you could group multiple callbacks into one class

class MySpecialCallback: SpecialCallback, () -> Unit {
    override fun onError() = TODO()
    override fun onSuccess() = TOOD()
    override operator fun invoke() = onSuccess()
}

but in that case it’s not really necessary to implement a functional interface and might even lead to hard to find bugs (is invoke the onSuccess or onError case).
Also you can’t extend multiple functional interfaces that just differ in their parameter/return types. They have to differ in the number of arguments.

class Foo: (Int) -> Unit, (String) -> Unit {
    override fun invoke(i: Int) = println(i)
    override fun invoke(s: String) = println(s)
}

generates this compiler error

Type parameter P1 of ‘Function1’ has inconsistent values: Int, String
A supertype appears twice

2 Likes