Reputation: 3081
I'm working with a Java API now converted into multiplatform Kotlin. It used to use java.lang.Optional
as the return type of many calls. I understand this is not the idiomatic Kotlin-way (see discussion) but this is an existing API, Optional stays (also it isn't a bad choice for the Java-facing client). My question is how?
Note: The code only needs to return Optional.of(x)
or return Optional.empty()
to the external API. Any internal uses will be purged.
expect
/actual
/typealias
to use the real Optional
class when available??
suffix)Upvotes: 3
Views: 786
Reputation: 147951
At this point, Kotlin doesn't allow providing an actual typealias
for an expected class with a companion object
by using a Java class with matching static
declarations. Follow this issue for updates: KT-29882.
For now, you can workaround that by declaring the factory functions separately, outside the expected Optional
class, as follows:
expect class Optional<T : Any> {
fun get(): T
fun isPresent(): Boolean
/* ... */
}
expect object Optionals {
fun <T : Any> of(t: T): Optional<T>
fun empty(): Optional<Nothing>
}
That should not necessarily be an object
, you could just use top-level functions.
Then, on the JVM, you would have to provide an actual typealias
for the Optional
class and, additionally, provide the trivial actual implementation for the Optionals
object:
actual typealias Optional<T> = java.util.Optional<T>
actual object Optionals {
actual fun <T : Any> of(t: T): Optional<T> = java.util.Optional.of(t)
actual fun empty(): Optional<Nothing> = java.util.Optional.empty()
}
As for not providing an implementation for the non-JVM platforms, I doubt it's possible, as that would require some non-trivial compile-time transformations of the Optional
usages to just the nullable type. So you would want something like this:
actual typealias Optional<T> = T?
which is now an error:
Type alias expands to
T?
, which is not a class, an interface, or an object
So you actually need a non-JVM implementation. To avoid duplicating it for every non-JVM target, you can declare a custom source set and link it with the platform-specific source sets, so they get the implementation from there:
build.gradle.kts
kotlin {
/* targets declarations omitted */
sourceSets {
/* ... */
val nonJvmOptional by creating {
dependsOn(getByName("commonMain"))
}
configure(listOf(js(), linuxX64())) { // these are my two non-JVM targets
compilations["main"].defaultSourceSet.dependsOn(nonJvmOptional)
}
}
}
Then, inside this custom source set (e.g. in src/nonJvmOptional/kotlin/OptionalImpl.kt
) you can provide an actual implementation for the non-JVM targets.
Here's a minimal project example on Github where I experimented with the above: h0tk3y/mpp-optional-demo
Upvotes: 2