Provide an Instance as its interface in Koin - kotlin

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() }

Related

Implement a regular interface in a spring data repository interface

I'm using a Repository that extends a spring data JpaRepository and would like to let it extend another interface.
Previously, my db repository looked like this:
interface PublicTransportPricingZoneRepository : JpaRepository<PublicTransportPricingZone, Long> {
}
I have now created another interface TransitTicketRepo as defined below
interface TransitTicketRepo {
fun findPossibleTickets(geometry: Geometry): Collection<TransitTicket>
}
and now would like to implement the interface with a default method in PublicTransportPricingZoneRepository. I've tried to solve this by changing the code of my PublicTransportPricingZoneRepository to
interface PublicTransportPricingZoneRepository : JpaRepository<PublicTransportPricingZone, Long>, TransitTicketRepo {
fun findPossibleTickets(geometry: Geometry): Collection<TransitTicket> {
// do something
return emptyList()
}
}
but get the following error message when starting the application.
org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.Collection PublicTransportPricingZoneRepository.findPossibleTickets(Geometry); Reason: Failed to create query for method public abstract java.util.Collection...
I'm assuming the solution is to somehow tell spring data to stop auto-generating a query for findPossibleTickets but have been unable to find out how.
You can do it this way
In short.:
#Repository
// Your bean that will be glued together by spring
interface PublicTransportPricingZoneRepository extends JpaRepository<...>, PublicTransportPricingZoneCustomRepository {
// Other methods, but everything must be annotated with #Query or possible for spring to guess what it needs to do from its name (google derived methods)
}
interface PublicTransportPricingZoneCustomRepository {
// define custom methods
List<String> nameIsNotImportant();
}
#Service
// naming here is important - it must be names as interface + Impl, otherwise spring won't pick it up
// apart from that, it's a regular bean - you can Autowire, etc
class PublicTransportPricingZoneCustomRepositoryImpl implementats PublicTransportPricingZoneCustomRepository {
// Implement custom methods
#Overridden
public List<String> nameIsNotImportant() {
// impl
}
}

Explicit type assignment in Koin singleton

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

I can't create a new instance of a ClassMirror by calling the defaullt constructor

I have two classes
class ESVAPI extends BibleProvider {
ESVAPI() : super('esvapi', true, {'esv'});
...methods
}
abstract class BibleProvider {
...fields
BibleProvider(this.name, this._requiresKey, this._versions) {
Bible.addProvider(this, _versions.toList());
}
}
I intend to have multiple classes extend the abstract class, so I want to create a method that creates an instances of each of BibleProvider's subclasses, I created one here:
ClassMirror classMirror = reflectClass(BibleProvider);
List<DeclarationMirror> subClassMirrors = currentMirrorSystem()
.libraries
.values
.expand((lib) => lib.declarations.values)
.where((lib) {
return lib is ClassMirror &&
lib.isSubclassOf(classMirror) &&
lib != classMirror;
}).toList();
DeclarationMirror subClassDec = subClassMirrors[0];
ClassMirror ESVCLASS = reflectClass(subClassDec.runtimeType);
var esvObj = ESVCLASS.newInstance(const Symbol(''), []);
But on ESVCLASS.newInstance I receive this exception:
No constructor '_ClassMirror' declared in class '_ClassMirror'
I'm thinking that this may have to do with how I call the superclass in the Constructor with "hard coded" values. If this is the case, is there a way to call the subclass' constructor and have it call the super constructor? I'm not entirely sure. Anyone familiar with reflections know what may be the case?
Change the last three lines to:
ClassMirror subClassDec = subClassMirrors[0] as ClassMirror;
var esvObj = subClassDec.newInstance(const Symbol(''), []);
...
print(esvObj.reflectee.runtimeType); // ESVAPI
You are reflecting on something that is already a mirror, so your ESVCLASS becomes the class mirror of the class _ClassMirror itself, not the subclass of BibleProvider you found above.
Just use the subClassDec class mirror directly.

Is it a good idea to place the code of instance a class in a interface in Kotlin?

The Code A is from the project android/architecture-components-samples.
The author place the code of instance a class DefaultServiceLocator in the interface ServiceLocator.
In my mind , normally a interface should not include any implement code.
Is it a good idea to place the code of instance a class in a interface in Kotlin?
Code A
interface ServiceLocator {
companion object {
private val LOCK = Any()
private var instance: ServiceLocator? = null
fun instance(context: Context): ServiceLocator {
synchronized(LOCK) {
if (instance == null) {
instance = DefaultServiceLocator(
app = context.applicationContext as Application,
useInMemoryDb = false)
}
return instance!!
}
}
/**
* Allows tests to replace the default implementations.
*/
#VisibleForTesting
fun swap(locator: ServiceLocator) {
instance = locator
}
}
...
}
open class DefaultServiceLocator(val app: Application, val useInMemoryDb: Boolean) : ServiceLocator {
...
}
In my mind , normally a interface should not include any implement code.
Welcome back from hibernation ;) Yes, you could achieve the same with interface + abstract class but you can have default implementation also as part of the interface for some time now in many languages. Which way you go is up to you, but if you have only one abstract class implementing your interface then it is often handy to be able to merge this into one file for sake of ease of future maintenance.
As per kotlin interfaces documentation:
Interfaces in Kotlin can contain declarations of abstract methods, as well as method implementations. What makes them different from abstract classes is that interfaces cannot store state. They can have properties but these need to be abstract or to provide accessor implementations.
So... there's no problem in using method implementations on the interfaces. That feature might offer you extra power (if you like and need to use it).

Koin - How to generify Singleton creation?

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.