I have a list of objects which are created using reflection in runtime and the types are not known in compile time. How can I define a singleton object in Koin using its class during runtime? Something like this:
val configurations: List<Any> = Configuration.scanAllConfigurations()
module {
configurations.forEach { single(it::class) { it } }
}
But unfortunately we can not explicitly define Class in single{}. Is there any solution to this?
Using bind can solve the issue:
single { it } bind it.javaClass.kotlin
Related
I'm trying to follow the Gradle custom plugin documentation to create a plugin that can be configured.
My plugin code:
interface MyExtension {
var myValue: Property<String>
}
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create<MyExtension>("myExt")
}
}
in build.gradle.kts:
plugins {
`java-library`
}
apply<MyPlugin>()
the<MyExtension>().myValue.set("some-value")
Running this will give
Build file '<snip>/build.gradle.kts' line: 6
java.lang.NullPointerException (no error message)
Turns out the the<MyExtension>().myValue is null, so the set call fails. How do I do this correctly? Did I miss something in the documentation, or is it just wrong?
The documentation is not wrong. Properties can be managed by either you or by Gradle. For the latter, certain conditions have to be met.
Without managed properties
If you want to be completely in charge, you can instantiate any variables you declare yourself. For example, to declare a property on an extension that is an interface, it could look like this:
override fun apply(project: Project) {
val extension = project.extensions.create("myExt", MyExtension::class.java)
extension.myValue = project.objects.property(String::class.java)
}
Or you could instantiate it directly in the extension by making it a class instead:
open class MessageExtension(objects: ObjectFactory) {
val myValue: Property<String> = objects.property(String::class.java)
}
However, a property field is not really supposed to have a setter as the property itself has both a setter and a getter. So you should generally avoid the first approach and remove the setter on the second.
See here for more examples on managing the properties yourself.
With managed properties
To help you reduce boilerplate code, Gradle can instantiate the properties for you with what is called managed properties. To do use these, the property must not have a setter, and the getter should be abstract (which it implicitly is on an interface). So you could go back to your first example and fix it by changing var to val:
interface MyExtension {
val myValue: Property<String> // val (getter only)
}
Now Gradle will instantiate the field for you. The same thing works for abstract classes.
Read more about managed properties in the documentation here.
In Kotlin, a common use for object is using it for singletons, like:
object MyObject {
...
}
However, when using micronaut framework, the official documentation recommends using something like this:
#Singleton
class V8Engine : Engine {
override var cylinders = 8
override fun start(): String {
return "Starting V8"
}
}
Why can't I use simply object instead of using annotation #Singleton with a class?
With a #Singleton, Micronaut can automatically manage dependencies between beans. If you go with the other class in https://docs.micronaut.io/latest/guide/ioc.html#beans, translated to Kotlin:
#Singleton
class Vehicle(private val engine: Engine) {
public fun start() = engine.start()
}
It can't be just an object because takes a parameter.
This parameter is discovered by Micronaut to be the singleton instance of V8Engine, which needs that to be a #Singleton and not an object.
Of course, in this case you could just directly use V8Engine in Vehicle; but it's easier to change e.g. if you want Engine not to be a singleton anymore.
Why can't I use simply object instead of using annotation #Singleton
with a class?
You can use object instead of using #Singleton with a class. Micronaut won't manage instances for you, but that is allowed.
I have a class InteractorCache<T> that I would like to inject in different places using Koin.
I would like to create a singleton instance of that class based on the type T. So if I have 10 types T, I would like 10 different singletons.
So far I managed to do the above with the following code (this is an example with only 2 types, A and B):
val interactorAModule = module {
factory {
InteractorA(get())
}
}
val aCache = module {
single(named("A")){
InteractorCache<List<A>>()
}
}
val interactorBModule = module {
factory {
InteractorB(get())
}
}
val bCache = module {
single(named("B")){
InteractorCache<List<B>>()
}
}
This works but there is a lot of repetition as I have to create a new cache module (aCache, bCache) every time I create a new type. I would like to be able to do something like this instead:
val cacheModule = module{
single<T>{
InteractorCache<T>()
}
}
so there is only 1 declaration that works for any type T.
Is there a way to do this in Koin?
Although this is late but the idea of making generic or T a singleton is bad idea, when you declare a class singleton it will run a single instance, so runtime error would be InteractorCache() is incompatible or mismatched to InteractorCache() as the first class you would assign the T for example the class A InteractorCache() it would be fixed instance of A and cannot anymore assign to class B.
Lets say I have two interfaces like:
interface LetterClassifier
interface NumberClassifier
Then these interfaces would be applied to this class:
class Classifier() : LetterClassifier, NumberClassifier
Now, I want to provide these instances only as LetterClassifier and NumberClassifier and not as Classifier in Koin.
The way I think of doing this is by doing:
module {
val classifier = Classifier()
single<NumberClassifier> { classifier }
single<LetterClassifier> { classifier }
}
But I don't think this is the right way. Can someone guide me?
You could bind types to your definition like it is described on official article:
single { Classifier() } binds arrayOf(LetterClassifier::class, NumberClassifier::class)
If you want to exclude Classifier type at all you could do something like:
single<LetterClassifier> { Classifier() } bind NumberClassifier::class
The way you're doing it is in fact the right way! Here's another example from the Koin docs, doing the same thing:
class DataRepository()
interface Presenter
class MyPresenter(val repository : Repository) : Presenter
val myModule = module {
// Define a singleton for type DataRepository
single { DataRepository() }
// Define a factory (create a new instance each time) for type Presenter (infered parameter in <>)
// Resolve constructor dependency with get()
factory<Presenter> { MyPresenter(get()) }
}
One small thing to note when doing this: your approach immediately creates an instance at the time the module declaration is being processed, while placing the constructor calls in the single lambdas would create instances when needed:
single<NumberClassifier> { Classifier() }
single<LetterClassifier> { Classifier() }
Although this would create a separate single instance for both of the interfaces.
You can have a function or a Singleton to provide instance,
single<NumberClassifier> { Singleton.createClassifier() }
single<LetterClassifier> { Singleton.createClassifier() }
I have something like:
interface Options {
fun load(conf: JsonObject)
}
object BasicOptions : Options { }
object PersonOptions : Options { }
object CarOptions : Options { }
then I would like to get all Objects that implements Options interface and call load forEach.
fun main(args: Array) {
configFuture.whenComplete { config ->
options.forEach { it.load(config) }
}
}
This is not supported by the language, this is why there are Dependency Injection, Registry, or Service Lookup solutions.
If all objects are registered in a Dependency Injection framework, you may be able to iterate all objects registered to the framework and find the instances of your Interface.
This is likely to be rather inefficient - so cache the results to avoid the extra overhead.
Another approach is to use custom class loader and add this custom functionality. The simplest way is probably using the Reflections library, to scan and load these classes. E.g.:
val reflections = Reflections("my.project.prefix")
val allClasses = reflections.getSubTypesOf(Options::class.java)
The simplest, and most commonly used solution would be simply maintain your own "registry" e.g. a static object holding the list of all instances. It will require manually adding any new class implementing the interface - but it will be simple, performant, and robust solution.
Of course, you can!
Using the Reflections library it's really easy:
First, you need to find all the java classes that implement your interface "Objects". Then you convert the java classes to kotlin classes (KClass).
KClass has the property objectInstance that is null if the class does not have an object instance... otherwise IS the singleton object instance.
So:
val reflections = Reflections(ConfigurationBuilder())
val jList = reflections.getSubTypesOf(Options::class.java)
val kList = jList.map { it.kotlin }
val oList = kList.map { it.objectInstance }.filterNotNull()
;-)
You can always use the Factory pattern for creating new instances of Options, that way you can have your own OptionsFactory and your own caching mechanism.
That OptionsFactory after instantiating an object keeps it in cache (could be in-memory cache or DB..etc), then anytime you can ask the factory for it's cached instances when needed.
This is fairly better when your Options objects are created in run-time/upon demand.