Feature requests: Kotlin extensions classes

I love the language and use it everywhere regularly testing the limits of the syntax.
One problem I ran into is with extentions.
To extend a class from a library I have good options like extension functions and extension properties or adapters.
However all have limitations when we want to extend multiple classes with the same functionality.
For example I tried to implement a game lib and an IA project where adapter classes decreased the efficiancy significantly, extention functions and properties got harder to follow in every iteration and so on.

My suggestion is to introduce extention interface implementations.
Like I can create an interface and I can implement it as an extention for closed classes.
That way I get the flexibility and efficiency of extentions while I can provide compatibility with other new classes like with adapters.

The solution could be similar to how value classes implement interfaces.

I recently started learnign a little rust and there they already have a somewhat ok solutions that could be kotlinified.

syntax idea:

interface MyInterface{ … }
class MyClass{ … }

imp MyClass : MyInterface{ … }
or
imp MyClass.MyInterface{ … }
or
ext MyClass : MyInterface{ … }
or
ext MyClass.MyInterface{ … }
or
ext class MyClass : MyInterface{ … }
or
extension class MyClass : MyInterface{ … }

The limitation compared to extension methods is that it is not generic and the type can not be nullable. The nullability is not important here in my opinion but it would be nice to make type parameters available so it can extend all implementers of an interface by a new interface.

I’m not sure if I read you correctly, but what you described sounds to me like a wrapper class, optionally a value class. Value class is basically a way to use 3rd party type A as it would be our type B with its own functions. How does your suggestion differ from value classes?

The problem of value classes is that they hide the original functionality of the class and require constant wrapping and unwrapping in the code. In my suggestion you have access to the original functionality and the extended functionality at the same time without a lot of boilerplate code.

I think the language constraints make this impossible. Extension functions are basically just syntactic sugar that get turned into static functions that take the receiver as the first parameter. But extension interfaces… how would that work in Java code/JVM byte code?

final class MyClass {
    public void dummyFunction(instance: MyClass) {
    }
}

How exactly would an extension interface work? It could create a wrapper class, like MyClassWrapper, but MyClassWrapper is NOT an instance of MyClass, nor does it extend MyClass, so you couldn’t pass it to dummyFunction.

1 Like

If I get the OP right, I see this more like a scoping/context feature. Like we can mark a property of an object (or even multiple properties), so it is automatically added to the context when we reference the outer object. Something like this:

class Foo {
    fun foo() {}
}

class Wrapper(@context val wrapped: Foo) {
    fun bar() {}
}

val wrapper = Wrapper(Foo())
wrapper.foo() // compiled to: wrapper.wrapped.foo()
wrapper.bar()

By using this together with value classes, it works as an “extension class”. We see Wrapper in the code, we can use both its functions and functions of Foo. Internally it is just Foo, its members are resolved directly and members of Wrapper are similar to extension functions (because this is how members of value classes work).

I didn’t read KEEP-259 thoroughly, but I think it doesn’t provide this.

Oh so it’s like… automatically creating a whole bunch of extension functions, I guess? I guess that’d kind of work. It’s a sort of convenience feature to group your extension functions together, meaning you still wouldn’t be able to access anything private or protected on the class you’re wrapping.

I’m not really sure how valuable this is, though? Couldn’t you just define all your extension functions in one file?

Or is the advantage here making sure that you have the same function signature across multiple classes? You’d still have to provide all the implementations, but…

Yes, the context based solutions is way closer to what I imagined. We might need a new concept that is other than interface implementation tho. Interfaces have to purpose: provide common type to handle multiple types together and to insure that a class has all functions required for a behaviour. Here the problem is that we only want to insure that the class has all the functions required, but even if we can not make it implement the interface. So basically we compile to a bunch of extention functions but for all classes extended.

Exactly. The goal is to extend multiple similar but final classes at the same time. This could be used forexample to extend all kind of arrays: Array<*>, BooleanArray, ByteArray… with the same functionality without having any function missing from any of them. Or extending number types… so on.

One more thing. If your main concern is that you have a large number of extensions and you would like to somehow group them together and use selectively, you can try this approach:

fun main() {
    val foo = Foo()
    foo.foo1() // compile error
    with (FooExtensions1) {
        foo.foo1() // ok
    }
}

class Foo

object FooExtensions1 {
    fun Foo.foo1() {}
}

Although, this is not perfect. IntelliJ stills recognizes all extensions on Foo and suggests importing them.

1 Like

Actually if this object can implement an interface that can have extention functions… Than that might solve my problem. I will try it! It should work. Even if it is a bit hacky.

Concept

class MyClass

interface MyInterface<T>{
fun T.myExtention()
}

object SpecificExtention : MyInterface<MyClass>{
fun MyClass.myExtetnion(){ ... }
}
1 Like

It works!

Hmm, I’m not sure what you did exactly, but I wouldn’t expect it to do anything useful. SpecificExtention is a singleton, it doesn’t reference any specific instance of MyClass, so even if you pass it somewhere where MyInterface is required, you will have to pass MyClass as well.

My idea was to use SpecificExtention simply as a namespace/context/scope that provides extensions over MyClass. But we still need a MyClass instance.

My goal was not to use SpecificExtention as an instance of MyClass, but to make multiple classes extended safly with the same functionality. For example I have different array types: Array<*>, ByteArray… If I want to extend all than I create one interface and separate object for each that extend them. This way if I want to add a new functionality I add it to the interface and I will be forced to implement it for all of the classes I target.

This is not equivalent to my original concept which can be solved by wrappers, but it is more efficient since it does not involve any wrapping of the classes I want to extend.

The original concept can be implemented tho with the context based solution you proposed. And it should be also supported by some syntactic sugar in my opinion.

This suggestion has come up time and time again for Kotlin, and while it would be pretty cool, operations like something is Interface sound like a nightmare to implement in the compiler if you can add interface implementations by extension.

1 Like

That’s actually pretty clever… nice work. You’ve achieved exactly what you wanted; defining a contract of extension methods and then provided specific implementations of those methods for a specific type. I might steal this idea, if I ever have a good use case for it…