Не может ввести мульти-связывание с котлином и кинжалом - PullRequest
2 голосов
/ 29 марта 2019

У меня есть следующие определения:

@Module
class WeaverDataModule {
    // Provide the three pumps from providers
    // All of them still explicitly mark 'Pump' as their return type
    @Provides @IntoSet fun providesPump(thermosiphon: Thermosiphon) : Pump = thermosiphon
    @Provides @IntoSet fun providesAnotherPump(suctionBased: SuctionBased) : Pump = suctionBased
    @Provides @IntoSet fun providesGenericPump(genericPump: GenericPump) : Pump = genericPump
}

@Component(modules = [WeaverDataModule::class])
interface WeaverData {
    // Get the CoffeeMaker
    fun coffeeMaker(): CoffeeMaker
    // Get the list of pumps
    fun getPumps() : Set<Pump>
}

interface Pump

// The three pumps
class Thermosiphon @Inject constructor(val heater: Heater) : Pump
class SuctionBased @Inject constructor() : Pump
class GenericPump @Inject constructor() : Pump

// Some random heater
class Heater @Inject constructor()

В моем коде, когда я делаю следующее:

val cm = DaggerWeaverData.builder().build().getPumps()

Я получаю три насоса, как и ожидалось.Однако, когда я пытаюсь внедрить его в какой-то другой класс:

class CoffeeMaker @Inject constructor(
    private val heater: Heater,
    private val pump: Set<Pump>
) {
    fun makeCoffee() =
        "Making coffee with heater ${heater::class.java} and using pumps" +
                " ${pump.map { it::class.java }.joinToString(",")}"
}

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

e: .../WeaverData.java:7: error: [Dagger/MissingBinding] java.util.Set<? extends weaver.Pump> cannot be provided without an @Provides-annotated method.                    
public abstract interface WeaverData {
                ^
      java.util.Set<? extends weaver.Pump> is injected at
          weaver.CoffeeMaker(…, pump)
      weaver.CoffeeMaker is provided at
          weaver.WeaverData.coffeeMaker()

Я пытался также ввести Collection<Pump>, ноЯ все еще получаю похожую ошибку.В документах кинжала о мультисвязывании пример (на Java) показывает следующее:

class Bar {
  @Inject Bar(Set<String> strings) {
    assert strings.contains("ABC");
    assert strings.contains("DEF");
    assert strings.contains("GHI");
  }
}

, что я и делаю.А для внедрения на основе конструктора в Kotlin он работает нормально, потому что следующие компилируются и работают, как и ожидалось:

class CoffeeMaker @Inject constructor(
    private val heater: Heater
) {
    fun makeCoffee() =
        "Making coffee with heater ${heater::class.java}"
}

Так что я немного растерялся из-за того, как мне получить это мультисвязывание сработа.

1 Ответ

1 голос
/ 29 марта 2019

Получается, что вам нужно сделать:

class CoffeeMaker @Inject constructor(
    private val heater: Heater,
    private val pumps: Set<@JvmSuppressWildcards Pump>
) {
    fun makeCoffee() =
        "Making coffee with heater ${heater::class.java} with pumps ${pumps.map { it::class.java }.joinToString(",")}"
}

Это потому, что Set определяется в Kotlin как Set<out E>, что переводится на Java как Set<? extends Pump>. С точки зрения теории типов, Set<? extends Pump> отличается от Set<Pump>, и поэтому Даггер (вероятно) отказывается рассматривать Set<Pump> как инъекцию для Set<? extends Pump>, что является справедливым и правильным поведением.

Проблема, с которой мы столкнулись, состоит в том, что для любой из этих коллекций, поскольку они являются неизменяемыми по умолчанию, объявление типа Set<X> будет преобразовано в Set<? extends X>, поскольку неизменяемая коллекция имеет только ссылки на разрешенный тип при возвратах и следовательно, является ковариантным. Чтобы проверить эту теорию, также работает следующее:

class CoffeeMaker @Inject constructor(
    private val heater: Heater,
    private val pumps: MutableSet<Pump>
) {
    fun makeCoffee() =
        "Making coffee with heater ${heater::class.java} with pumps ${pumps.map { it::class.java }.joinToString(",")}"
}

Обратите внимание на использование MutableSet, которое определяется как MutableSet<E> : Set<E> .... Это, вероятно, не то, что нужно использовать, потому что я сомневаюсь, что этот набор на самом деле изменчив. Поэтому нам нужно, чтобы компилятор kotlin воспринимал Set<out E> как Set<E> (в этом случае назначаемое значение действует, но никак не наоборот). Поэтому мы используем аннотацию @JvmSuppressWildcards. Я надеюсь, что это поможет кому-то еще сталкиваться с подобными проблемами.

...