Interop¶
Annotations¶
Metro supports user-defined annotations for common annotations. This means that a user doesn’t necessarily have to use Metro’s annotations if they’re introducing it to an existing codebase. Support varies depending on the annotation’s use case.
Compile-only annotations are mostly supported. This includes the following:
@AssistedFactory
@AssistedInject
@Assisted
@BindsInstance
@Binds
@ContributesBinding
@ContributesTo
@DependencyGraph.Factory
@DependencyGraph
@ElementsIntoSet
@Inject
@IntoMap
@IntoSet
@MapKey
@Module
@Multibinds
@Provides
@Qualifier
@Scope
These are configurable via Metro’s Gradle extension.
metro {
interop {
assisted.add("dagger/assisted/Assisted")
}
}
For Dagger and KI specifically, there are convenience helper functions.
metro {
interop {
includeDagger()
includeKotlinInject()
includeAnvil()
}
}
@DependencyGraph
is replaceable but your mileage may vary if you use Anvil or modules, since Metro’s annotation unifies Anvil’s @MergeComponent
functionality and doesn’t support modules.
Similarly, @ContributesBinding
is replaceable but there are not direct analogues for Anvil’s @ContributesMultibinding
or kotlin-inject-anvil’s @ContributesBinding(multibinding = …)
as these annotations are implemented as @ContributesInto*
annotations in Metro.
binding
in Metro uses a more flexible mechanism to support generics, but interop with Anvil’s boundType: KClass<*>
property is supported.
Components¶
Metro graphs can interop with components generated by Dagger and Kotlin-Inject. These work exclusively through their public accessors and can be depended on like any other graph dependency.
@DependencyGraph
interface MetroGraph {
val message: String
@DependencyGraph.Factory
fun interface Factory {
fun create(
@Includes daggerComponent: DaggerComponent
): MetroGraph
}
}
@dagger.Component
interface DaggerComponent {
val message: String
@dagger.Component.Factory
fun interface Factory {
fun create(@Provides message: String): DaggerComponent
}
}
Conversely, kotlin-inject and Dagger components can also depend on Metro graphs.
@DependencyGraph
interface MessageGraph {
val message: String
// ...
}
// Dagger
@Component(dependencies = [MessageGraph::class])
interface DaggerComponent {
val message: String
@Component.Factory
fun interface Factory {
fun create(messageGraph: MessageGraph): DaggerComponent
}
}
// kotlin-inject
@Component
abstract class KotlinInjectComponent(
@Component val messageGraph: MessageGraph
) {
abstract val message: String
}
Runtime¶
Enabling dagger interop also enables more advanced runtime interop with Dagger/Javax/Jakarta’s Provider
/Lazy
types.
metro {
interop {
includeDagger()
}
}
This specifically enables two features.
- Interop with Dagger/Javax/Jakarta’s
Provider
andLazy
runtime intrinsics. - Interop with generated Dagger factories for constructor-injected classes, assisted-injected classes, and Dagger modules. This means that an upstream class or module that was processed with the dagger compiler and has a generated Java factory class can be natively reused by Metro.
Note this also automatically adds an extra interop-dagger
dependency to support this scenario.
Enabling this feature also enables interop with Dagger’s @BindsOptionalOf
annotation.
Diagnostics¶
When interoping with annotations that are written in Kotlin and have parameters, it may be unsafe to rely on positional arguments. Metro’s own annotations often have the same indices, but not always! If you want to be super safe, you can enable the interopAnnotationsNamedArgSeverity
to WARN
or ERROR
to report diagnostics for positional arguments in any custom annotations that Metro is configured to look at.
Why only Kotlin annotations?
This is because the Kotlin compiler doesn’t support positional arguments for annotations that are written in Java.