kotlin-inject
kotlin-inject copied to clipboard
Implementation for scoped components
I was looking at the generated code today (the following example comes from the tests). And I didn't completely get why it uses a LazyMap for ScopedComponent wheras I guessed a Lazy property was enough. So I'm just interested to see the main idea, thanks!
public class InjectTypeAccessComponent() : TypeAccessComponent(), ScopedComponent {
override val _scoped: LazyMap = LazyMap()
override val string: String
get() = _scoped.get("kotlin.String") {
provideString()
}
override val `class`: IFoo
get() = _scoped.get("me.tatarka.inject.test.IFoo") {
provideClass()
}
// ...
}
It's because you can have a scoped inject in a different module than the component so it can't know ahead of time every thing to create a property for. The map allows them to be created dynamically
Oh, I see I'm adding an example from the tests to be sure
I see that the generated code is directly using the scoped LazyMap so it makes sense, it moves the creation closer to the usage (typically not in ExternalParent nor ExternalChild class, even if they define the bind)
To complete a bit the overall question, I wanted to define a scope of long-living services for my app and I thought it would be nice to avoid recreating it (typically for caches). And I was expecting the implementation to be closer to the non-scoped one or closer to a manual DI (with components defining fields with eager or lazy values directly). That's fine, thanks!
@Component
abstract class NestedExternalScopedComponent(@Component val parent: ExternalChildComponent) {
abstract val foo: IExternalFoo // is defined in the parent: external child component
}
/* generated */
public class InjectNestedExternalScopedComponent(
parent: ExternalChildComponent,
) : NestedExternalScopedComponent(parent) {
override val foo: IExternalFoo
get() = with(parent.parent) {
(this as ScopedComponent)._scoped.get("me.tatarka.inject.test.module.ScopedExternalFoo") {
ScopedExternalFoo()
}.bind
}
}
// and the parent.parent is
public class InjectExternalParentComponent() : ExternalParentComponent(), ScopedComponent {
override val _scoped: LazyMap = LazyMap()
}
I checked the integration tests and I saw some benchmark I was curious so I ran it, with an additional test of an eager version to see the maximum speed possible I will probably push it (except if you think it is not needed)
Yeah the benchmark was to see if there was a meaningful performance difference from using a map vs lazy prop, the result was there was not.
with an additional test of an eager version to see the maximum speed possible
Not sure what you mean by this?
I just have in mind the most basic class possible
class Example {
val a: A = A() // instead of lazy { A() }
}
That has different semantics though, being lazy is important for the expected behavior.
I agree, but still I think it can be interesting to measure