How can I create a Factory that produces objects based on a type or a Class object? - kotlin

In my Kotlin application I have a few entities (and a data class for each of them) and for each entity I have a service object implementing generic Service<T> interface.
I want to create a Factory of services that will return me a proper service based on a parameter which is a type of entity I want to have a service for. In Java I would pass a Class object into the factory which I could obtain from a static context of the entity class eg. Entity.class but I can't do that in Kotlin. How can I create a Factory that will produce me objects based on a type of an entity?

You're looking for KClass:
Say you have the following classes:
abstract class Parent(val name: String)
class ChildA : Parent("A")
class ChildB : Parent("B")
Then your factory may look like this:
fun <T : Any> factory(c: KClass<T>): T {
return c.createInstance()
}
fun main(args: Array<String>) {
val childA = factory(ChildA::class)
val childB = factory(ChildB::class)
println(childA.name) // A
println(childB.name) // B
}
But there's a better way using reified:
inline fun <reified T : Any> factory(): T {
return T::class.createInstance()
}
Then you can call it like this:
val childA = factory<ChildA>()
val childB = factory<ChildB>()
println(childA.name)
println(childB.name)
Note that without using reified we couldn't do T::class

Related

Kotlin class generics without duplication

Consider an abstract class:
abstract class PubSubSubscriber<T : Any>(private val topic: KClass<T>) : BackgroundFunction<PubSubMessage> {
abstract fun consume(payload: T)
override fun accept(message: PubSubMessage, context: Context) {
val json = String(Base64.getDecoder().decode(message.data.toByteArray()))
val payload = objectMapper.readValue(json, topic.java)
consume(payload)
}
}
And implementation:
class MySubscriber : PubSubSubscriber<Payload>(Payload::class) {
Is there a way to define such abstract class so that I don't have to repeat twice the Payload and Payload::class in the class definition?
Yes, with some reflection.
At construction time, we can extract the type parameter and assign it to a property that no longer needs to be given to the constructor:
abstract class PubSubSubscriber<T : Any> {
val topic: KClass<T> = extractTypeParam<T>(0).kotlin
private fun <X> extractTypeParam(paramIdx: Int): Class<X> {
require(PubSubSubscriber::class.java == javaClass.superclass) {
"PubSubSubscriber subclass $javaClass should directly extend PubSubSubscriber"
}
#Suppress("UNCHECKED_CAST")
return (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[paramIdx] as Class<X>
}
abstract fun consume(payload: T)
override fun accept(message: PubSubMessage, context: Context) {
val json = String(Base64.getDecoder().decode(message.data.toByteArray()))
val payload = objectMapper.readValue(json, topic.java)
consume(payload)
}
Note the following limitations:
A) this solution works only if MySubscriber directly extends from PubSubSubscriber. However, the given code can detect if that's not the case and warn about it (at runtime). In such cases, there are the following solutions:
MySubscriber falls back to providing a duplicate argument (essentially what you already had)
the direct superclass of MySubscriber can provide a similar detection mechanism
B) You call reflection code every time a MySubscriber instance is created. This may be too slow in certain contexts, but for many this is unproblematic.

Kotlin get runtime class from generic implementation

I'm trying to build a register component that takes in a list of QueryHandlers in my application.
Below you can see the queryHandler and Query definition. They both implement an interface with generics.
class MyQueryHandler(): QueryHandler<MyQuery, MyReturnType>
class MyQuery(val query: String) : Query<MyReturnType>
Now, I have a component in Spring that is using BeanPostProcessor to catch the Bus bean definition and call to it the function registerQueryHandler for each of the query handler he gets in the list.
registerQueryHandler signature is
fun <Q : Query<V>, V> registerQueryHandler(aClass: Class<Q>, queryHandler: QueryHandler<Q, V>)
so it takes the query handler and the class resulting from the query execution.
Below is the pseudocode of my BeanPostProcessor
class SimpleQueryBusPostProcessor(
private val queryHandlers: List<QueryHandler<Q<V>, V>>,
private val beanNameSelector: String
) : BeanPostProcessor {
override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
if (beanName == beanNameSelector && bean is SimpleMessageBus) {
queryHandlers.forEach {
bean.registerQueryHandler(#runtimeClassForQ<V>onIt, it)
}
}
return bean
}
}
My problem is on "#runtimeClassForQonIt" I would like to know at runtime, given a specific QueryHandler, which concrete implementation of the Query it is implementing to register it into the Bus.
Any idea on how can I achieve this?
I've tried with some inline function to get it using reified like below, but I only get back the interface that T implements (Query) instead of the concrete class
private inline fun <reified T: Q<P>, P> getClazz(handler: QueryHandler<T, P>) : Class<T> {
return T::class.java
}

NullPointerException inside Kodein

I'm trying out Kotlin with Kodein and in my current project I'm getting a NPE inside Kodein and I'm don't know why.
I have some data classes and matching repositories which deliver a list of them:
data class Cat(val name: String)
data class Dog(val name: String)
interface Repository<T> {
val all: List<T>
}
interface CatRepository : Repository<Cat>
interface DogRepository : Repository<Dog>
The implementations of these repositories are currently backed by a master class:
data class AnimalData(val cats: List<Cat>, val dogs: List<Dog>)
I created an abstract base class for the repositories:
abstract class AnimalDataRepository<T>(override val kodein: Kodein) : Repository<T>, KodeinAware {
private val animalData: AnimalData by instance()
abstract val property: (AnimalData) -> List<T>
override val all: List<T> = animalData.let(property)
}
So that the repository implementations look like this:
class CatRepositoryImpl(override val kodein: Kodein) : CatRepository, AnimalDataRepository<Cat>(kodein) {
override val property = AnimalData::cats
}
Setting this up and running with:
fun main() {
val kodein = Kodein {
bind<AnimalData>() with singleton { AnimalData(listOf(Cat("Tigger")), listOf(Dog("Rover"))) }
bind<CatRepository>() with singleton { CatRepositoryImpl(kodein) }
}
val catRepository: CatRepository by kodein.instance()
println(catRepository.all)
}
leads to a NPE inside Kotlin:
Exception in thread "main" java.lang.NullPointerException
at org.kodein.di.KodeinAwareKt$Instance$1.invoke(KodeinAware.kt:176)
at org.kodein.di.KodeinAwareKt$Instance$1.invoke(KodeinAware.kt)
at org.kodein.di.KodeinProperty$provideDelegate$1.invoke(properties.kt:42)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at AnimalDataRepository.getAnimalData(KodeinExample.kt)
at AnimalDataRepository.<init>(KodeinExample.kt:27)
at CatRepositoryImpl.<init>(KodeinExample.kt:30)
at KodeinExampleKt$main$kodein$1$2.invoke(KodeinExample.kt:40)
at KodeinExampleKt$main$kodein$1$2.invoke(KodeinExample.kt)
at org.kodein.di.bindings.Singleton$getFactory$1$1$1.invoke(standardBindings.kt:130)
...
I'm not clear why this happens. It has something to do with the use of the "property" mapping lamba in AnimalDataRepository, because when I don't use that it works fine.
Complete code as gist: https://gist.github.com/RoToRa/65d664d2d7497ddbf851a1be019f631d
this is because in your class AnimalDataRepository you have defined:
override val all: List<T> = animalData.let(property)
While Kodein is working lazily, thus all is defined before, that's why animalData is null. However, you can fix this by doing:
override val all: List<T> by lazy { animalData.let(property) }

Kotlin generics with in produces Type mismatch when compiling

I´m working on a code with generics and when I use an in I got a TypeMismatch when compiling.
The code is the following:
open class A
class B:A()
data class DataContainer(val a:String,
val b:A)
interface Repo<T:A>{
fun setParam(param:T)
fun getParam():T
}
abstract class RepoImp<T:A>:Repo<T>{
private lateinit var parameter:T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class BRepo:RepoImp<B>()
class Repo2(val repo: Repo<in A>){
fun process(b:DataContainer){
repo.setParam(b.b)
}
}
val repoB = BRepo()
val repo2 = Repo2(repoB)// Here I got: Type mismatch: inferred type is BRepo but Repo<in A> was expected
I also tried changing the attribute repo from Repo2 to Repo<*>
Since BRepo is a Repo<B>, it is not a Repo<in A>, (but it would satisfy Repo<out A>).
In other words, a Repo<in A> must be able to accept setParam(A()), but BRepo.setParam() can only accept a B or subclass of B.
Or to put it another way, BRepo is a Repo<B>, which is a tighter restriction on the type than Repo<A> when it comes to writing values (but looser restriction when reading values).
The reason class Repo2(val repo: Repo<*>) doesn't work is that Repo<*> is essentially a Repo<in Nothing/out A>. You can't call setParam() on a Repo<*> with any kind of object.
There's a design flaw in your code that you can't fix simply by changing Repo2's constructor signature. As it stands now, Repo2 needs to be able write A's to the object you pass to it, and a BRepo by definition does not support writing A's, only B's. You will need to make at least one of your class's definitions more flexible about types.
It might be easier to understand the covariance limitation with more common classes:
val stringList: MutableList<String> = ArrayList()
var anyList: MutableList<in Any> = ArrayList()
anyList.add(5) // ok
anyList = stringList // Compiler error.
// You wouldn't be able to call add(5) on an ArrayList<String>
Basically MutableList<String> is not a MutableList<in Any> the same way Repo<B> is not a Repo<in A>.
The Repo2 class expect to consume only type A, use Repo2<T : A>(val repo: Repo<in T>)
open class A
class B : A()
class C : A()
class D : A()
class BRepo : RepoImp<B>()
class CRepo : RepoImp<C>()
class DRepo : RepoImp<D>()
interface Repo<T : A> {
fun setParam(param: T)
fun getParam(): T
}
abstract class RepoImp<T : A> : Repo<T> {
private lateinit var parameter: T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class Repo2<T : A>(val repo: Repo<in T>) {
fun process(b: DataContainer<T>) {
repo.setParam(b.b)
}
}
data class DataContainer<T : A>(
val a: String,
val b: T
)
fun main() {
val repoB = BRepo()
val repoC = CRepo()
val repoD = DRepo()
val repo2 = Repo2(repoB)
val repo3 = Repo2(repoC)
val repo4 = Repo2(repoD)
repo2.process(DataContainer("Process B type", B()))
repo3.process(DataContainer("Process C type", C()))
repo4.process(DataContainer("Process D type", D()))
println(repo2.repo.getParam())
println(repo3.repo.getParam())
println(repo4.repo.getParam())
}

Searching a workaround for kotlin empty data class primary constructor

With given kotlin code :
sealed class Event(val id:String= UUID.randomUUID().toString(), val timestamp:Instant = Instant.now())
data class BarEvent(val additionalInfo:String):Event()
object FooEvent:Event()
// data class CorrectFooEvent():Event() // invalid kotlin
fun main(args: Array<String>) {
val b1 = BarEvent("b1")
val f1 = FooEvent
Thread.sleep(1000)
val b2 = BarEvent("b2")
val f2 = FooEvent
println("${b1.id} ${b1.timestamp} $b1")
println("${f1.id} ${f1.timestamp} $f1")
println("${b2.id} ${b2.timestamp} $b2")
println("${f2.id} ${f2.timestamp} $f2")
}
There is no issue with BarEvent.
But as FooEvent has no more parameter than the ones in Event, I would like it to have empty constructor. It's not authorized for data class, so I made it an object. But object is singleton, so it doesn't behave as an instanciated event.
The only workaround that I see (keeping the class as a data class) is something like :
sealed class Event(open val id:String= UUID.randomUUID().toString(), open val timestamp:Instant = Instant.now())
data class FooEvent(override val id:String= UUID.randomUUID().toString(), override val timestamp:Instant = Instant.now()):Event()
But it's not very elegant.
Just change FooEvent to a normal class, and add (or generate them using your IDE) toString(), hashCode() and equals(Object) if needed:
class FooEvent: Event() {
override hashCode() = ...
override equals(other: Object) {
...
}
override toString() = ...
}
To make the event a data class, simply add an unused property to it. Not pretty, but as short as it can be in Kotlin at the moment:
data class FooEvent(val dummy: Unit = Unit) : Event()
There seems to be no intention to remove this limitation soon:
Data class without arguments deprecated in 1.0. Why?
Suggestion for parameterless data class