@Binds - Dagger2

インターフェースを実装したクラスがあり、それをインターフェース経由でバインドしたいとき、通常以下のようにしてました。


@Module
object BookPresenterModule {
  @Provides @JvmStatic
  fun provideBookPresenter(bookPresenter: BookPresenterImpl): BookPresenter = bookPresenter
}

これは @Inject 付きコンストラクタと一緒に使われるモジュールの一部です。これは、以下のように記述するべきです。


@Module
abstract class BookPresenterModule {
  @Binds abstract fun bindBookPresenter(bookPresenter: BookPresenterImpl): BookPresenter
}

これまでどおり「インターフェースにその実装をバインドしたいとき」に使うことができます。

コード生成がされず、どこからもコールされないのに、きちんと連携情報として利用されるのが良いところです。

Dagger 2.4 で登場したにもかかわらず、なぜか公式ユーザーガイドでは説明されていません。

👉 Release Dagger 2.4 · google/dagger 

以下、公演動画などから学ぶことができます。




@Provides メソッドはすべて static に - Dagger2

👉 Dagger 2 on Android: The Official Guidelines You Should Be Following 

any module whose @Provides methods are all static, the implementation doesn’t need an instance at all.

すべてのモジュールの @Provides メソッドはすべて static。実装部分にインスタンスは必要ない。

👉 User's Guide 

モジュールの @Provides メソッドが static であれば、Daggerはそれをインスタンス化する必要がない。

もし、そうできないのならそのモジュールは「状態」を持っているので修正するべき。

Warning: it’s discouraged for modules to have state; this can lead to unpredictable behavior. Moreover, module instances in general are rarely useful. We have considered removing them.

警告: モジュールが「状態」を持つことは推奨されません。これにより、予期しないふるまいが発生する可能性があります。

👉 Dagger Core Semantics 

Kotlin で static なメソッドを書く場合2つの方法があります。

トップレベルで object を使う方法。


@Module
object DataModule {
  @JvmStatic @Provides fun provideDiskCache() = DiskCache()
}

companion object を使う方法。


@Module
abstract class DataModule {
  @Binds abstract fun provideCache(diskCache: DiskCache): Cache

  @Module
  companion object {
    @JvmStatic @Provides fun provideDiskCache() = DiskCache()
  }
}

👉 Kotlin+Dagger best practices/documentation/pain points · Issue #900 · google/dagger 

この2つの方法に対してJakeさんがコメントしています。

First of all, this speed benefit will be extraordinarily minor. If you are looking to make your app faster then use a profiler. You will find 100 easier optimization targets.
Beyond that, don't use companion object for modules. Use object. In that case the instance will be unused and its initialization code will be removed by R8 and the methods will be truly static and can also be inlined just like Java.

まず第一に、この速度の利点は非常にわずかです。アプリをより高速にしたい場合は、プロファイラーを使用してください。 100の簡単な最適化ターゲットがあります。
さらに、モジュールには「コンパニオンオブジェクト」を使用しないでください。オブジェクトを使用します。その場合、インスタンスは使用されず、その初期化コードはR8によって削除され、メソッドは真に静的であり、Javaと同様にインライン化することもできます。

👉 Kotlin+Dagger best practices/documentation/pain points · Issue #900 · google/dagger 

インスタンスが不要な場合、R8の静的化によりオブジェクトのメソッドを静的にすることができますが、Daggerはコンパイル時にそれらを静的にする必要があるため、@JvmStaticアノテーションは必要です。


関連ワード:  AndroidKotlin開発


Dagger2 Dependency Graph を視覚化する「Daggraph」

クローズドな一般国内環境にて、

他人の書いたコードというのは、

非常に読みづらいものです。

Dagger のようなバイトコードを生成するコンパイルタイムなフレームワークであればさらにその特性は増してしまいます。

図で視覚化して概要を捉えましょう。



👉 dvdciri/daggraph: Dagger dependency graph generator for Android Developers 

シンプルなコマンドラインツールなので入れてみて損はないでしょう。