Introduction to kotlin-inject
kotlin-inject is a compile-time dependency injection library for Kotlin, designed to simplify and streamline the process of managing dependencies in Kotlin applications. By operating at compile time, it reduces runtime overhead, leading to more efficient performance and easier-to-manage codebases.
Core Concepts
At the heart of kotlin-inject is the concept of a Component
, which serves as a container for your injectable classes. A Component
is declared with the @Component
annotation on an abstract class. Through the use of this component, kotlin-inject generates implementations that manage the creation and provisioning of dependencies.
Using Components
-
Declaration: In a component, you declare abstract read-only properties or functions to specify the types of objects you require. kotlin-inject takes care of figuring out how to construct these types.
-
Providing Dependencies: Use the
@Provides
annotation to create functions or properties that supply external dependencies. Explicitly defining return types is recommended to ensure clarity.
Example Usage
Here’s a simple usage example:
@Component
abstract class AppComponent {
abstract val repo: Repository
@Provides
protected fun jsonParser(): JsonParser = JsonParser()
protected val RealHttp.bind: Http
@Provides get() = this
}
@Inject
class RealHttp : Http
@Inject
class Api(private val http: Http, private val jsonParser: JsonParser)
@Inject
class Repository(private val api: Api)
val appComponent = AppComponent::class.create()
val repo = appComponent.repo
In this example, kotlin-inject automatically manages the creation of Api
and Repository
instances.
Advanced Features
Component Arguments
Components can accept constructor arguments annotated with @Provides
, enabling these values to be part of the dependency graph.
@Component
abstract class MyComponent(@get:Provides protected val foo: Foo)
Qualifiers
When you have multiple instances of the same type, use @Qualifier
annotations to differentiate between them.
@Qualifier
annotation class Named(val value: String)
@Inject
class InjectedClass(@Named("one") dep1: Dep, @Named("two") dep2: Dep)
Scopes
You can manage the lifespan of instances with scopes, ensuring certain dependencies are reused as long as a component remains in memory.
@Scope
@Target(CLASS, FUNCTION, PROPERTY_GETTER)
annotation class MyScope
@MyScope
@Component
abstract class MyComponent()
Multi-bindings
Collect multiple bindings into a Map
or Set
with @IntoMap
and @IntoSet
annotations.
Default Arguments
If a dependency is not available in the graph, you can declare default arguments, allowing your application to fallback to these defaults.
@Inject class MyClass(val dep: Dep = Dep("default"))
Download & Setup
To integrate kotlin-inject, use Kotlin Symbol Processing (KSP) in your project setup:
- settings.gradle: Add repositories for required plugins.
- build.gradle: Define plugins and dependencies, including
kotlin-inject
.
plugins {
id("org.jetbrains.kotlin.jvm") version "1.9.0"
id("com.google.devtools.ksp") version "1.9.0-1.0.13"
}
dependencies {
ksp("me.tatarka.inject:kotlin-inject-compiler-ksp:0.7.2")
implementation("me.tatarka.inject:kotlin-inject-runtime:0.7.2")
}
This setup helps you get started with integrating kotlin-inject into your Kotlin projects for clean and efficient dependency management. By leveraging these features, developers can achieve cleaner architectures, reduced boilerplate code, and better-managed dependencies in their Kotlin applications.