Методы статического провайдера Dagger 2 в Котлине

В последних версиях кинжала 2 одним из улучшений является возможность использования статических методов обеспечения. Просто так:

@Provides
static A providesA() {
  return A();
}

Мне было интересно, как это сделать в котлине? Я пробовал

@Module
class AModule {
  companion object {
    @JvmStatic
    @Provides
    fun providesA(): A = A()
  }
}

Но я получаю сообщение об ошибке:

@Provides methods can only be present within a @Module or @ProducerModule

Я предполагаю, что здесь что-то происходит с объектом-компаньоном, однако я совершенно новичок в Kotlin, и я не уверен, как это можно сделать. Возможно ли это?

Спасибо!

Ответы

Ответ 1

Я не могу проверить это прямо сейчас, но я думаю, что это должно работать:

@Module
object AModule {
    @JvmStatic
    @Provides
    fun providesA(): A = A()
}

Ответ 2

Хотя я думаю, что решение zsmb13 лучше, я нашел другое решение, которое работает

@Module
class AModule {
  @Module
  companion object {
    @JvmStatic
    @Provides
    fun providesA(): A = A()
  }

  // add other non-static provides here
}

Однако обратите внимание, что будут два сгенерированных класса: AModule_ProvidesAFactory и AModule_Companion_ProvidesAFactory, а не один AModule_ProvidesAFactory класс для случая с объектом вместо класса с сопутствующим объектом

Ответ 3

Отличное объяснение, которое, похоже, одобрено Google, находится на https://github.com/google/dagger/issues/900

В частности, см.:

Статическое обеспечение может быть достигнуто через @JvmStatic. Я вижу, что есть два сценария:

top-level objects

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

Если у вас есть существующий класс модуль, вещи становятся немного страннее

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

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

Это работает следующим образом:

сопутствующий объект также должен быть аннотирован как @Module под капотом, компилятор kotlin будет дублировать эти статические методы в класс DataModule. Кинжал увидит их и будет относиться к ним как регулярные статические поля. Кинжал тоже увидит их в спутнике объект, но этот "модуль" получит код gen из кинжала, но будет помечен как "неиспользованный". В среде IDE это будет помечено как таковое, как provideDiskCache. метод будет помечен как неиспользованный. Вы можете сказать IntelliJ игнорировать это для аннотаций, аннотированных @Provides с помощью quickfix

Ответ 4

Для статического подхода мне нравится решение zsmb13.

Однако я пришел сюда, потому что хотел объединить @Provides и @Binds в одном модуле. Это невозможно сделать напрямую, но с двумя вложенными модулями (как указывал Омар Аль Халаби).

Я выбрал немного другой подход для объединения @Provides и @Binds:

@Module(includes = [MyModule.Bindings::class])
object MyModule {
    @Module
    interface Bindings {
        @Binds
        fun bindA(a: AImpl): A
    }

    @Provides
    @JvmStatic
    fun provideB(): B = BImpl()
}

Различия:

  • Внешний модуль - это объект, обеспечивающий статические функции. Это требует непреднамеренного использования companion object.
  • Внутренний модуль содержит абстрактные привязки. Здесь я могу использовать интерфейс, который освобождает модификатор abstract как в классе, так и в функции.
  • Внешний модуль включает в себя внутренний модуль, поэтому мне не нужно включать внутренний модуль в другом месте.