Suggestion: platform specific methods in multiplatform classes

I’ve been using Kotlin for a multiplatform project (a web app with JVM and JS). The expect/actual feature is very useful, but I wish I could do something like this in a shared class:

class Thing {
    var x = ...
    fun foo() { ... }
    expect fun bar(): Baz
}

In my case I only want to implement bar() on the JVM. I can’t use an extension method because I have a class hierarchy of Things and I want the virtual dispatch on bar().

So, I’m wondering…

  • Do others also want this feature?
  • Is it already in the works.? I don’t see it anywhere.
  • Is there a nice workaround that obviates the need?
  • Are Kotlin users encouraged to flesh out ideas like this and submit KEEPs?

It seems there are two related but separate situations:

  1. You want methods in a common class to have different implementations on different platforms. This fits with expect/actual.
  2. You want to add methods to a common class on one particular platform. Maybe expect/actual is not ideal here, since the other actual methods would just throw an Exception.

Rob

If the class ‘Thing’ is common to both platforms, than any method of ‘thing’ must be on both platforms.
However, you can have a method that generates an exception if called on a platform where the method is not implemented (that is…implemented beyond throwing the exception)

To answer your specific questions:
declaring a ‘bar’ in the js module with ‘TODO()’ as the only code is about as close as i see as possible to a solution and it is possible already, as it throws a ‘not implemented’ exception if the function is called.

Perhaps this gives the workaround you need?

If a function, value or variable only needs to be available on a specific platform, do not add it to the common type definition. Simply add the member to the actual platform implementation of the common type.

See these types for an example:

I don’t think that’s possible. You have to expect the entire class, in which case I would have to duplicate code in JVM and JS. My example code, which has Thing in the common module and expect on just the bar method, does not compile.

Okay, so you’re splitting the class into a base and subclass. This seems like a good workaround, but the way you said “the common type definition” made it sound like this is the standard way to do platform specific declarations. There is no mention of this technique in the Kotlin docs on the topic. They show a single class split into expected and actual, not two classes related by inheritance.

Splitting a class like this seems like the best workaround, if we can’t have individual expected members of common classes. The downside is that it will almost double the number of classes in my hierarchy (already about 20), so that’s a lot of boilerplate.

Another workaround is to have a method in the common class immediately call a top level expected function. But this only works if the function can be implemented without access to private class members.

I still think it would be an improvement if Kotlin allowed expected members in a common class.

No, I don’t. I am saying that the platform-specific implementation class (actual) of a common class (expect) can have platform-specific methods. There is no inheritance needed. It just so happens that the function in the example I posted overrides a function from a base type, but this is not a requirement.

Simple create a minimal project and try different techniques to see what happens.

I have no idea what you mean here. That is exactly what you can do with a common class.

Common types can do the following:

  • Provide member implementations that are common to all platforms.
  • Declare that some members need to be implemented in a platform-specific manner.

I don’t see how to do that. Can you show me some example code with a single class that has both common and platform specific members? This does not compile for me:

// in common module
expect class A {
    fun fooCommon() {   }  // error: Expected declaration most not have a body
    fun bar()
}

Nor the other way:

// in common module
class A {
   fun commonFun() { }
   expect fun bar()
}

The only way I see to do it is like I saw in your example before where you put the common code in one class, and then have a subclass that is expected and implemented differently in JVM and JS.

I recall there is a feature request for that: https://youtrack.jetbrains.com/issue/KT-20427. If implemented, it would allow to provide default common implementations for expect declarations, including members of expect classes. Currently that is not possible.

1 Like

Okay, thanks for the clarification. I’ve voted for that issue but also created a second one here: https://youtrack.jetbrains.com/issue/KT-34668. I think it’s a bit different, but I understand if it ends up marked as a duplicate.

Looks like you can just use different interfaces for this.

As an example:

interface AllPlatforms {
    fun commonMethodForAllPlatforms()
}

interface JVMPlatform : AllPlatforms  {
    fun jvmMethod()
}

object PlatformApiHolder {
     val api : AllPlatforms
         get(): {
                    if(JVM) {
                         return object : JVMPlatform {                              
                         }
                    }
         }
}

fun someJvmMethod() {
    val jvmApi = PlatformApiHolder.api as JVMPlatform ;
}

Finally you can define different interfaces for each platform. Moreover, the most of validation will be in compile time.