Basic Usage¶
Table of Contents¶
- Single interface binding
- Multiple interfaces
- Generic interfaces
- Parent class binding
- Object binding
- Selecting specific binding targets
- Qualifier annotations
- How it works
- Troubleshooting
- Requirements for annotated classes and objects
Single Interface Binding¶
Annotate a concrete class or Kotlin object that implements an interface or extends a base class
with @AutoBinds, and the processor generates a Hilt module automatically.
interface UserRepository {
suspend fun getUser(id: Long): User
}
@AutoBinds
class UserRepositoryImpl @Inject constructor(
private val api: UserApi,
private val db: UserDao,
) : UserRepository {
override suspend fun getUser(id: Long): User {
return db.getUser(id) ?: api.fetchUser(id).also { db.insert(it) }
}
}
Generated code:
@Module
@InstallIn(SingletonComponent::class)
internal interface UserRepositoryImplModule {
@Binds
fun bindToUserRepository(impl: UserRepositoryImpl): UserRepository
}
As a result, you don't need to write Hilt binding modules manually, and Hilt can now
inject UserRepository anywhere.
Multiple Interfaces¶
If a class implements multiple interfaces, bindings are generated for all of them:
@AutoBinds
class LoggerImpl @Inject constructor() : Logger, Closeable {
override fun log(message: String) { /* ... */ }
override fun close() { /* ... */ }
}
Generated code:
@Module
@InstallIn(SingletonComponent::class)
internal interface LoggerImplModule {
@Binds
fun bindToLogger(impl: LoggerImpl): Logger
@Binds
fun bindToCloseable(impl: LoggerImpl): Closeable
}
Generic Interfaces¶
Interfaces with type parameters are supported. The generated binding preserves the concrete type argument:
interface Repository<T> {
fun getAll(): List<T>
}
@AutoBinds
class StringRepository @Inject constructor() : Repository<String> {
override fun getAll(): List<String> = listOf("hello")
}
Generated code:
@Module
@InstallIn(SingletonComponent::class)
internal interface StringRepositoryModule {
@Binds
fun bindToRepository(impl: StringRepository): Repository<String>
}
Parent Class Binding¶
@AutoBinds also works when a class extends an open or abstract parent
class directly. A binding is generated for the parent class in the same way as
for interfaces:
Generated code:
@Module
@InstallIn(SingletonComponent::class)
internal interface HomeServiceModule {
@Binds
fun bindToBaseService(impl: HomeService): BaseService
}
A class can mix parent class and interface supertypes - a binding is generated
for each direct supertype (excluding Any):
abstract class BaseService
interface Trackable
@AutoBinds
class HomeService @Inject constructor() : BaseService(), Trackable
This generates a single module with @Binds functions for both BaseService
and Trackable.
Object Binding¶
Kotlin object declarations are supported. Since objects are singletons managed
by the language itself, no @Inject constructor is needed. The processor
generates @Provides functions (instead of @Binds) that return the object
instance directly:
Generated code:
@Module
@InstallIn(SingletonComponent::class)
internal object NoOpNavigatorModule {
@Provides
fun bindToNavigator(): Navigator = NoOpNavigator
}
This is useful for stateless implementations like no-op stubs, or default strategies.
All features work with objects in the same way as with classes: installIn,
bindTo, qualifiers, scopes, custom components, and annotation aliases. Objects
also work with @AutoBindsIntoSet and @AutoBindsIntoMap.
Selecting Specific Binding Targets¶
By default, @AutoBinds generates a binding for every direct supertype. Use
bindTo to restrict which supertypes are included, or to target a
grandparent class that is not a direct supertype:
interface Repo
interface Closeable
interface Auditable
// Only binds to Repo; Closeable and Auditable are excluded
@AutoBinds(bindTo = [Repo::class])
class RepoImpl @Inject constructor() : Repo, Closeable, Auditable
interface GrandParent
open class Parent : GrandParent
// Binds to GrandParent even though it's not a direct supertype
@AutoBinds(bindTo = [GrandParent::class])
class Child @Inject constructor() : Parent()
bindTo accepts any transitive supertype of the annotated class. The
processor emits a compile-time error if a listed type is not a supertype
at all.
When bindTo is empty (the default), all direct supertypes are used.
Qualifier Annotations¶
A JSR-330 qualifier annotation placed on the annotated class is forwarded to the
generated @Binds function, allowing Hilt to distinguish multiple bindings of the
same type:
@Named("prod")
@AutoBinds
class ProdApiService @Inject constructor() : ApiService
@Named("mock")
@AutoBinds
class MockApiService @Inject constructor() : ApiService
Custom @Qualifier annotations are supported in the same way. See
Qualifiers for the full guide, including usage with
@AutoBindsIntoSet, factory bindings, and annotation aliases.
How It Works¶
Hilt AutoBind is a KSP annotation processor
that runs at compile time. For each class or object annotated with @AutoBinds, it:
- Finds all direct supertypes (implemented interfaces and extended parent
classes, excluding
Any). - For classes: generates an
internal interfaceHilt module with a@Bindsfunction for each supertype. - For objects: generates an
internal objectHilt module with a@Providesfunction for each supertype, returning the object instance directly. - Installs the module in the appropriate Hilt component (see Scopes and Components).
The generated modules are internal, so they don't pollute your module's
public API.
Troubleshooting¶
If you encounter KSP errors when building a project that uses Hilt/Dagger or this library, try the following steps in order:
- Stop the Gradle daemon and rebuild:
Then trigger a build again from Android Studio or the command line.
- If the error persists - clean and rebuild:
In Android Studio: Build -> Clean Project, then Build -> Rebuild Project.
Or from the command line:
Stale KSP caches and daemon state are a common cause of spurious build failures after dependency or annotation changes.
Requirements for Annotated Classes and Objects¶
A class annotated with @AutoBinds (in default mode, without a factory) must:
- Be a concrete class (not an interface or enum).
- Be non-abstract (no
abstractmodifier). - Not be an inner class (no
innermodifier). - Have a primary constructor annotated with
@Inject. - Implement at least one interface or extend a non-
Anyparent class.
A Kotlin object annotated with @AutoBinds must:
- Implement at least one interface or extend a non-
Anyparent class. - Not use the
factoryparameter (objects are their own instances and do not need a factory).
The processor emits a compile-time error if any of these conditions are not met.