https://kotlinlang.org logo
#multiplatform
Title
# multiplatform
m

Michael

03/24/2023, 9:33 AM
Currently, there is a problem with platform-specific code. If you want to provide an extension to a common class only on a specific platform, this can become very tedious So let's say we have the following common class
Copy code
public class HttpBuilder {
  public fun addFile(channel: ByteReadChannel)
}
But we want to provide interop with `java.nio`we have two options 1. Use an Extension function in jvm source set
Copy code
public fun HttpBuilder.addFile(file: Path) = addFile(file.readChannel())
2. Create an abstraction
Copy code
public abstract class HttpBuilderBase {
  public fun addFile(channel: ByteReadChannel)
}

public expect class HttpBuilder : HttpBuilderBase

// jsMain
public actual class HttpBuilder : HttpBuilderBase()

// jvmMain
public actual class HttpBuilder : HttpBuilderBase() {
  public fun addFile(file: Path) = addFile(file.readChannel())
}
Both options have their advantages, the main advantage if the extension function is, that it is very easy to use, however it is often not an option for some use cases. e.g. if you are converting a JVM library to MPP and want to preserve binary compatibility you cannot use an extension function as that would break binary compatibility by removing the old member function A possible solution would be to add something similar to @OptionalExpectation to non annotations 1. Optional Expectations for members
Copy code
public class HttpBuilder {
  public fun addFile(channel: ByteReadChannel)
  @OptionalExpectation
  public fun addFile(file: PlatformType) 
}
Then this function just won't be callable from common source sets, but only from platform source sets declaring an actual type for it However this might need to add some platform specific types that we don't want to have in common, therefor i added that `PlatformType`which acts simmilar to the
definedExternally
property 2. @PlatformOnly
Copy code
public class HttpBuilder {
  public fun addFile(channel: ByteReadChannel)
  @JvmOnly
  public fun addFile(file: Path) = TODO() 
}
This allows us to declare platform code in common that is then only compiled to the desired classes 3. "actual" for non-expect classes (my favourite)
Copy code
actual class HttpBuilder {
  public fun addFile(file: Path) = TODO() 
}
Another option would be the ability to declare actual classes for non-expecting classes, this could be limited to only adding "extensions", but they will be added to the actual class at runtime so it preserves binary compatibility
r

russhwolf

03/24/2023, 3:32 PM
Using
actual typealias
can help with this. Make an
expect
declaration with just the things you want in common, then typealias to the thing that already exists on JVM so you know it didn't change. There's limitations (some things don't match up well, eg
protected
declarations) and it's hard if you have two different existing implementations that you're trying not to break, but it's a decent option for the common scenario where you have JVM code you don't want to break and are migrating it to be shared.
m

Michael

03/24/2023, 6:43 PM
typealiases can helpp however this den't really help in this case, where we have a kotlin class which has some helper function for a JVM type we want to make common
9 Views