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.
How do we use expect/actual/typealias to use the real Optional class when available?
Is there a way to avoid re-implementing a fake Optional class on non-Java targets (i.e. work idiomatically with nullable? suffix)
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
Related
I am relatively new Kotlin and Generics kind of give me a headache. I have the following architecture made out of:
A few data classes
A generic interface to process data
Implementations of that processing interface for each data type
A generic processing job class containing the data to be processed and it's appropriate processor
A global (singleton) processor which implements the processing interface, takes processing jobs and just delegates the processing to the job processor. It doesn't care about the data itself at all.
The simplified code looks like this
class DataOne
class DataTwo
interface DataProcessor<in T> {
fun process(o: T)
}
class DataOneProcessor: DataProcessor<DataOne> {
override fun process(o: DataOne) = println("Processing DataOne")
}
class DataTwoProcessor: DataProcessor<DataTwo> {
override fun process(o: DataTwo) = println("Processing DataTwo")
}
class ProcessingJob<T>(val data: T, val processor: DataProcessor<T>)
object GlobalProcessor: DataProcessor<ProcessingJob<Any>> {
override fun process(job: ProcessingJob<Any>) = job.processor.process(job.data)
}
fun main() {
GlobalProcessor.process(ProcessingJob(DataOne(), DataOneProcessor()))
}
In the main function I get a compiler error
Type mismatch.
Required: ProcessingJob<Any>
Found: ProcessingJob<DataOne>
I understand why this happens: A DataProcessor of DataOne, viewed as a DataProcessor of Any could be asked to process DataTwos and for type safety this is not allowed.
Can you give me any suggestions on how/what to change to make it compile and achieve the required result? Thanks for your time!
There are two problems here.
First, Any isn't actually the top-level type. Any implies not null, but T is unconstrained, which means it can be a nullable type. In this case you can use *, or you could also specify the type as Any?.
Change the signature of the GlobalProcessor to this:
object GlobalProcessor: DataProcessor<ProcessingJob<*>> {
override fun process(job: ProcessingJob<*>): ...
The second problem is that the implementation of process can't take advantage of the generic information from the job in order to know that the job.processor and the job.data are compatible. It just sees two objects of unknown type. To let it know they share a compatible type, you need to capture that type as a type variable. We can't add a generic type parameter to the existing method, because it has to match the signature of the interface method, but we can add a new private method that introduces the generic parameter.
Here's the GlobalProcessor with both the required changes.
object GlobalProcessor: DataProcessor<ProcessingJob<*>> {
override fun process(job: ProcessingJob<*>) = processGeneric(job)
private fun <T> processGeneric(job: ProcessingJob<T>) = job.processor.process(job.data)
}
Consider the following interface
interface EntityConverter<in A, out B> {
fun A.convert(): B
fun List<A>.convert(): List<B> = this.map { it.convert() }
}
I want to use it in a spring boot application where specific implementations get injected so that the extension function becomes usable on the type.
However this doesn't work. The compiler does not resolve the extension function.
Note that you're defining extension functions that are also member functions of the EntityConverter type. You should take a look at this part of the doc for information about how this works.
Essentially, in order to use them, you need 2 instances in scope:
the dispatch receiver (an instance of EntityConverter<A, B>)
the extension receiver (an instance of A or List<A>, where A matches the first type parameter of the EntityConverter in scope)
You can use with() to bring the EntityConverter in scope so you can use convert on your other instances using the usual . syntax:
val converter = object : EntityConverter<Int, String> {
override fun Int.convert() = "#$this"
}
val list = listOf(1, 2, 3)
val convertedList = with(converter) {
list.convert()
}
println(convertedList) // prints [#1, #2, #3]
Now you have to decide whether this kind of usage pattern is what makes most sense for your use case. If you'd prefer more "classic" calls without extensions (converter.convert(a) returning a B), you can declare your functions as regular methods taking an argument instead of a receiver.
Bonus: functional interface
As a side note, if you add the fun keyword in front of your EntityConverter interface, you can create instances of it very easily like this:
val converter = EntityConverter<Int, String> { "#$this" }
This is because your converter interface only has a single abstract method, making it easy to implement with a single lambda. See the docs about functional interfaces.
I'm not sure if you can mention extension functions as a part of interface, because it's like static functions.
I'd recommend to put "common" function in interface with A typed parameter. Then just put extension method for list nearby.
interface EntityConverter<in A, out B> {
fun convert(a: A): B
}
fun <A, B> EntityConverter<A, B>.convert(list: List<A>): List<B> = list.map { convert(it) }
Update
I wasn't aware about possibility of inheritance of extension methods in Kotlin. And about its overriding as well. So my answer could be just an alternative of using extension methods.
I was looking into co- and contravariance in several programming languages' collection libraries, and stumbled over Kotlin's Set interface.
It is documented as
interface Set<out E> : Collection<E>
which means it is covariant – only "producing" E objects, following the Kotlin documentation, not consuming them.
And Set<String> becomes a subtype of Set<Any>.
Yet, it has those two methods:
abstract fun contains(element: E): Boolean
abstract fun containsAll(elements: Collection<E>): Boolean
So when I create a class implementing Set<String>, I have to implement (beside others) contains(String). But later someone can use my class as a Set<Any> and call set.contains(5).
I actually tried this:
class StringSet : Set<String> {
override val size = 2
override fun contains(element: String): Boolean {
println("--- StringSet.contains($element)")
return element == "Hallo" || element == "World"
}
override fun containsAll(elements: Collection<String>) : Boolean =
elements.all({it -> contains(it)})
override fun isEmpty() = false
override fun iterator() = listOf("Hallo", "World").iterator()
}
fun main() {
val sset : Set<String> = StringSet()
println(sset.contains("Hallo"))
println(sset.contains("xxx"))
//// compiler error:
// println(set.contains(5))
val aset : Set<Any> = sset
println(aset.contains("Hallo"))
println(aset.contains("xxx"))
// this compiles (and returns false), but the method is not actually called
println(aset.contains(5))
}
(Run online)
So it turns out that Set<String> is not a "real" subtype of Set<Any>, as the set.contains(5) works with the second but not the first.
Actually calling the contains method even works at runtime – just my implementation will never be called, and it just prints false.
Looking into the source code of the interface, it turns out that the two methods are actually declared as
abstract fun contains(element: #UnsafeVariance E): Boolean
abstract fun containsAll(elements: Collection<#UnsafeVariance E>): Boolean
What is going on here?
Is there some special compiler magic for Set?
Why is this not documented anywhere?
Declaration-site covariance in the form of the out modifier misses a useful use case of making sure that an instance passed as an argument is generally sensible to pass here. The contains functions are a good example.
In the particular case of Set.contains, the #UnsafeVariance annotation is used to ensure that the function accepts an instance of E, as passing an element that is not E into contains makes no sense – all proper implementation of Set will always return false. The implementations of Set are not supposed to store the element passed to contains and thus should never return it from any other function with the return type E. So a properly implemented Set won't violate the variance restrictions at runtime.
The #UnsafeVariance annotation actually suppresses the compiler variance conflicts, like using an out-projected type parameter in an in-position.
Its motiviation is best described in this blog post:
#UnsafeVariance annotation
Sometimes we need to suppress declaration-site variance checks in our classes. For example, to make Set.contains typesafe while keeping read-only sets co-variant, we had to do it:
interface Set<out E> : Collection<E> {
fun contains(element: #UnsafeVariance E): Boolean
}
This puts some responsibility on the implementor of contains, because with this check suppressed the actual type of element may be anything at all at runtime, but it’s sometimes necessary to achieve convenient signatures. See more on the type-safety of collections below.
So, we introduced the #UnsafeVariance annotation on types for this purpose. It’s been deliberately made long and stands out to warn agains abusing it.
The rest of the blog post also explicitly mentions that the signature of contains using #UnsafeVariance improves type-safety.
The alternative to introducing #UnsafeVariance was to keep contains accepting Any, but this option lacks the type check on contains calls that would detect erroneous calls with elements that can't be present in the set due to not being instances of E.
coming across a sample with a class and a function and trying to understand the koltin syntax there,
what does this IMeta by dataItem do? looked at https://kotlinlang.org/docs/reference/classes.html#classes and dont see how to use by in the derived class
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
class DerivedStreamItem(private val dataItem: IMeta, private val dataType: String?) :
IMeta by dataItem {
override fun getType(): String = dataType ?: dataItem.getType()
fun getData(): DerivedData? = getDataItem()
private inline fun <reified T> getDataItem(): T? = if (dataItem is T) dataItem else null
}
for the reference, copied the related defines here:
interface IMeta {
fun getType() : String
fun getUUIDId() : String
fun getDataId(): String?
}
class DerivedData : IMeta {
override fun getType(): String {
return "" // stub
}
override fun getUUIDId(): String {
return "" // stub
}
override fun getDataId(): String? {
return "" // stub
}
}
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
There is some good documentation on reified type parameters, but I'll try to boil it down a bit.
The reified keyword in Kotlin is used to get around the fact that the JVM uses type erasure for generic. That means at runtime whenever you refer to a generic type, the JVM has no idea what the actual type is. It is a compile-time thing only. So that T in your example... the JVM has no idea what it means (without reification, which I'll explain).
You'll notice in your example that you are also using the inline keyword. That tells Kotlin that rather than call a function when you reference it, to just insert the body of the function inline. This can be more efficient in certain situations. So, if Kotlin is already going to be copying the body of our function at compile time, why not just copy the class that T represents as well? This is where reified is used. This tells Kotlin to refer to the actual concrete type of T, and only works with inline functions.
If you were to remove the reified keyword from your example, you would get an error: "Cannot check for instance of erased type: T". By reifying this, Kotlin knows what actual type T is, letting us do this comparison (and the resulting smart cast) safely.
(Since you are asking two questions, I'm going to answer them separately)
The by keyword in Kolin is used for delegation. There are two kinds of delegation:
1) Implementation by Delegation (sometimes called Class Delegation)
This allows you to implement an interface and delegate calls to that interface to a concrete object. This is helpful if you want to extend an interface but not implement every single part of it. For example, we can extend List by delegating to it, and allowing our caller to give us an implementation of List
class ExtendedList(someList: List) : List by someList {
// Override anything from List that you need
// All other calls that would resolve to the List interface are
// delegated to someList
}
2) Property Delegation
This allows you to do similar work, but with properties. My favorite example is lazy, which lets you lazily define a property. Nothing is created until you reference the property, and the result is cached for quicker access in the future.
From the Kotlin documentation:
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
I'm pretty new in Kotlin language, but I have just encountered some strange behavior that didn't have in other languages, so I wanted to ask why I can't do something like this:
fun <T> methodName()
{
// whatev~
}
fun <T, K> methodName()
{
// whatev~
}
This code throws an error of "Conflicting overloads".
In other languages, for example C# I can do this and it's a pretty neat trick to have only one method that work for one or multiple types at the same time.
The only workaround I've found it's adding in each new method that I do an optional parameter that I'll never use, like:
fun <T> methodName()
{
}
fun <T, K> methodName(crappyParam: String = "")
{
}
The two methods would have the same signature in JVM type system (which doesn't support generics), which isn't allowed.
A JVM language could "mangle" such methods, e.g. giving them different names in bytecode. A JVM implementation of C# would have to.
But Kotlin doesn't. And doing so would hurt interoperability with Java, which is one of Kotlin's major requirements.