google/dagger

Annotate interface with default Bind implementation

krocard opened this issue · 2 comments

TL;DR: Is it possible to annotate an interface with the implementation to inject by default?

Problem

An extremely common pattern in our (and many other) code base is to create an interface for every class.
This allows to inject test/mock/fake implementations, notably in unit tests. Here is an example:

interface Foo { /** ... */ }
class DefaultFoo @Inject constructor(/** ... */) {  /** ... */ } // Only implementation in production

// Somewhere far, in the Dagger setup
@Bind Foo bindFoo(DefaultFoo foo)

The @Bind line is an error prone boilerplate that we would really like to avoid. We have 100s of those @Bind lines across dozen of components and modules.
It encourage writing class that depend on the implementation instead of the interface, to avoid having to write it. Which is the opposite goal of a dependency injection framework 😅

Suggestion

Would it be possible to remove the need for a separate @Bind annotation and instead annotate the interface with the implementation? For example:

@DefaultBind(DefaultFoo::class)
interface Foo { /** ... */ }

See #3590, and the other linked issues

Thanks for the relevant link. #1164 linked via the other issue has probably most of the relevant discussion.

Since this suggestion is slightly different by being an annotation on the interface, I'll add that this is similar to Guice's @ImplementedBy which I think has proven to be somewhat controversial in practice. One of the main issues is that interfaces generally shouldn't really know about their implementations, and this also tends to be bad for projects that do finer grained dependency management, as now the interface drags in possibly unnecessary and usually heavier dependencies from the impl.