Kotlin: access companion data in generic function - kotlin

I have a simple task in Kotlin of listing enum values:
interface DisplayableEnum<E: Enum<E>> {
val displayValue: String
}
inline fun <reified T> printAllValues(selected: T?) where T: Enum<T>, T: DisplayableEnum<T> {
println("All Values:")
enumValues<T>().forEach {
println(it.displayValue)
}
selected?.let { println("\nSelected: ${it.displayValue}") }
}
////// USAGE
enum class MyEnum(override val displayValue: String): DisplayableEnum<MyEnum> {
A("value is A"),
B("value is B")
}
fun main() {
// with a selected value
f(MyEnum.A)
// without a selected value
f(null as MyEnum?)
}
Now imagine that all the enums I pass to printAllValues should also have a field called defaultValue. In case of MyEnum I would write it the following way:
enum class MyEnum(override val displayValue: String): DisplayableEnum<MyEnum> {
A("value is A"),
B("value is B");
companion object {
val defaultValue = A
}
}
So my question: is there a way to define such a contract in Kotlin?
Ideally, I would like to somehow define that contract in an interface, like DisplayableEnum above, and then somehow use it in printAllValues like this:
inline fun <reified T> printAllValues(selected: T) where T: Enum<T>, T: DisplayableEnum<T> {
println("All Values:")
enumValues<T>().forEach {
println(it.displayValue)
}
selected?.let { println("\nSelected: ${it.displayValue}") }
println("Default value: ${T.defaultValue???}"
}
The one thing that I don't want is using non-companion defaultValue, I always have to either pass it to the function manually (but why if the type contains all the info?) or, if made non-companion:
interface DisplayableEnum<E: Enum<E>> {
val displayValue: String
val defaultValue: E
}
then access it through an object - do something ugly like enumValues<T>().first().defaultValue.
I wonder if Kotlin has a solution in this case.

It's impossible to define abstract properties in companion object.
So it should be defined in interface directly.
The tricky part here is implementing of this interface without compiler warnings:
enum class MyEnum(override val displayValue: String) : DisplayableEnum<MyEnum> {
A("value is A"),
B("value is B");
override val defaultValue: MyEnum by lazy { A }
}
If enumValues<T>().first().defaultValue looks ugly to you, wrap it into auxilary function:
inline fun <reified T> enumDefaultValue(): T where T : Enum<T>, T : DisplayableEnum<T> =
enumValues<T>().first().defaultValue
//Usage:
println("Default value: ${enumDefaultValue<T>()}")

Related

Ktor reified type parametar

I created class with generic in kotlin and want to use receive with generic, but I have error when i want to call.recieve type from generic:
Can not use MType as reified type parameter. Use a class instead.
Code:
class APIRoute<EType : IntEntity, MType : Any> {
fun Route.apiRoute() {
post {
val m = call.receive<MType>()
call.respond(f(model))
}
}
}
How to fix it?
You need to provide the expected type to the receive() function. Due to type erasure in Java/Kotlin, the type of MType is unknown at runtime, so it can't be used with receive(). You need to capture the type as KType or KClass object when constructing APIRoute.
KClass is easier to use, however it works with raw classes only, it doesn't support parameterized types. Therefore, we can use it to create e.g. APIRoute<*, String>, but not APIRoute<*, List<String>>. KType supports any type, but is a little harder to handle.
Solution with KClass:
fun main() {
val route = APIRoute<IntEntity, String>(String::class)
}
class APIRoute<EType : IntEntity, MType : Any>(
private val mClass: KClass<MType>
) {
fun Route.apiRoute() {
post {
val m = call.receive(mClass)
call.respond(f(model))
}
}
}
Solution with KType:
fun main() {
val route = APIRoute.create<IntEntity, List<String>>()
}
class APIRoute<EType : IntEntity, MType : Any> #PublishedApi internal constructor(
private val mType: KType
) {
companion object {
#OptIn(ExperimentalStdlibApi::class)
inline fun <EType : IntEntity, reified MType : Any> create(): APIRoute<EType, MType> = APIRoute(typeOf<MType>())
}
fun Route.apiRoute() {
post {
val m = call.receive<MType>(mType)
call.respond(f(model))
}
}
}

Kotlin: generate a Factory by class

We're trying to do some generic processing in kotlin. Basically, for a given class, we want to get the related Builder object. i.a. for any object that extends a GenericObject, we want a Builder of that Object.
interface Builder<T : GenericObject>
object ConcreteBuilder: Builder<ConcreteObject>
We'd need a function that will return ConcreteBuilder from ConcreteObject
Our current implementation is a Map:
val map = mapOf<KClass<out GenericObject>, Builder<out GenericObject>>(
ConcreteObject::class to ConcreteBuilder
)
Then we can get it with:
inline fun <reified T : GenericObject> transform(...): T {
val builder = map[T::class] as Builder<T>
...
However this isn't very nice as:
we need an explicit cast to Builder<T>
the map has no notion of T, a key and a value could be related to different types.
Is there any better way to achieve it?
A wrapper for the map could be:
class BuilderMap {
private val map = mutableMapOf<KClass<out GenericObject>, Builder<out GenericObject>>()
fun <T: GenericObject> put(key: KClass<T>, value: Builder<T>) {
map[key] = value
}
operator fun <T: GenericObject> get(key: KClass<T>): Builder<T> {
return map[key] as Builder<T>
}
}
This hides the ugliness, while not completely removing it.
To use:
val builderMap = BuilderMap()
builderMap.put(ConcreteObject::class, ConcreteBuilder)
builderMap.put(BetonObject::class, BetonBuilder)
// builderMap.put(BetonObject::class, ConcreteBuilder) – will not compile
val builder = builderMap[T::class]

Is there a way to reduce/remove the redundant generic parameter in Kotlin?

Consider the following function:
abstract class ObservableCollection<E> : CoroutineScope, MutableCollection<E> {...}
class ObservableList<E>(override val backingSource: MutableList<E> = mutableListOf()) : ObservableCollection<E>(), MutableList<E> by backingSource {...}
inline fun <reified T : Event<E>, E> ObservableCollection<E>.on(scope: CoroutineScope = this, noinline consumer: suspend (T) -> Unit): Job =
events.buffer(Channel.UNLIMITED)
.filterIsInstance<T>().onEach {
runCatching { consumer(it) }.onFailure { ObservableCollection.logger.catching(it) }
}.catch { ObservableCollection.logger.catching(it) }
.launchIn(scope)
To call it, the first and general way (which comes in mind):
val list = ObservableList<Int>()
list.on<ElementAddEvent<Int>, Int> {
}
To make it more idiomatic (towards removing redundancy of passing explicit generics), we can have something like this:
list.on { event: ElementAddEvent<Int> ->
}
But is there a way to remove that explicit generic type <Int> in { event: ElementAddEvent<Int> -> as well? Since it is implicit, as coming from the type of ObservableList on which on method/function is called on.
Edit:
Event<E> is basically an interface to for all the possible events like ElementRemoveEvent<E>, ElementAddEvent<E>, ElementReplaceEvent etc.
interface Event<E> : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = collection.coroutineContext
val collection: ObservableCollection<E>
}
class ElementAddEvent<E>(
val element: E,
override val collection: ObservableCollection<E>
) : Event<E>
class ElementReplaceEvent<E>(
val newElement: E,
val oldElement: E,
override val collection: ObservableCollection<E>
) : Event<E>
// And so-on...
It has basically nothing to do with the question, so I didn't put it on.
Edit 2:
One way could be to make static functions in each of the event like:
class ElementAddEvent<E>(
val element: E,
override val collection: ObservableCollection<E>
) : Event<E> {
companion object {
fun <E> on(collection: ObservableCollection<E>, consumer: suspend (ElementAddEvent<E>) -> Unit) {
collection.on { event: ElementAddEvent<E> ->
consumer(event)
}
}
}
}
This will make possible to be callable as:
ElementAddEvent.on(list) {
}
But I think this will make too much noise in the sources, so I would prefer to use the second approach posted in the question itself (specifying type inside the lambda). If I missed something question is still open, you can let me know!

Is there any way to declare a scope extension to third party library kotlin class?

I'm trying a program build with Jetbrain/Exposed as ORM library and TornadoFX which is a kotlin version wrapper of JavaFX as UI Framework.
There is a problem that an entity's class property is delegated by Exposed.
object Paintings: UUIDTable() {
...
val description = text("description").default("")
...
}
class Painting(id: EntityID<UUID>) : UUIDEntity(id) {
...
var description by Paintings.description
...
}
And I'm also want to create an property delegated to JavaFX's property like this
class Painting(id: EntityID<UUID>) : UUIDEntity(id) {
...
var description by property<String>()
fun descriptionProperty() = getProperty(Painting::description)
...
}
There is a conflict here,so I'm trying to build a own delegate class to wrapper two framework's delegated.Maybe build something which can wrap two frameworks's delegate though a infix function extension.
class Painting(id: EntityID<UUID>) : UUIDEntity(id) {
...
var description by Paintings.description notify property<String>()
fun descriptionProperty() = getProperty(Painting::description)
...
}
And here is the problem that the operator setValue and getValue for delegate from Exposed is declare in Entity class
open class Entity<ID:Comparable<ID>>(val id: EntityID<ID>) {
...
operator fun <T> Column<T>.getValue(o: Entity<ID>, desc: KProperty<*>): T =
lookup()
operator fun <T> Column<T>.setValue(o: Entity<ID>, desc: KProperty<*>, value: T) {...}
If I declare a wrapper class it just cannot access the delegate operator for Column which is scoped in Entity class
//Global Extension instead of scoped to `Entity`
infix fun <T> Column<T>.notify(fxProperty: PropertyDelegate<T>) {
return DelegateWrapper(this,fxProperty)
}
class DelegateWrapper<T>(val column: Column<T>, val fxProperty: PropertyDelegate<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return column.getValue(...) <-cannot resolve getValue
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String)
{
...
}
}
As a work arround I guess I had to build a subclass of UUIDEntity and add those extension as member extension and nest class to make it work.
You can do that by limiting the type that the delegate can be used with to Entity.
This can be done by replacing thisRef: Any? with thisRef: Entity<ID> and calling the getValue extension in a thisRef.run { ... } block that provides an Entitiy<ID> receiver as follows:
class DelegateWrapper<T>(val column: Column<T>, val fxProperty: PropertyDelegate<T>) {
operator fun <ID : Comparable<ID>> getValue(
thisRef: Entity<ID>, // <-- here
property: KProperty<*>
): String =
thisRef.run { column.getValue(thisRef, property) }
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) = TODO()
}

Kotlin - How to find and cast an element by its type

I have a collection of objects which inherit Component and I want a function which finds an object by its concrete class and return it.
But Kotlin does not like the cast I do, and adding #Suppress("UNCHECKED_CAST") is ugly.
I have the following code:
open class GameObjectImpl : GameObject {
private val attachedComponents = mutableSetOf<Component>()
#Suppress("UNCHECKED_CAST")
override fun <TComponent : Component> getComponent(type: KClass<TComponent>): TComponent? {
return attachedComponents.find { type.isInstance(it) } as? TComponent
}
}
This should work for you:
open class GameObjectImpl : GameObject {
val attachedComponents = mutableSetOf<Component>()
override inline fun <reified TComponent : Component> getComponent(type: KClass<TComponent>): TComponent? {
return attachedComponents.filterIsInstance<TComponent>().firstOrNull()
}
}