Несоответствие типов черт Scala

У меня есть некоторая иерархия Kafka Channel, которую я использую в своем проекте:

Моя базовая черта:

trait SendChannel[A, B] extends CommunicationChannel {
  def send(data:A): B
}

Теперь у меня есть общая кафка, отправляющая канал вроде

trait CommonKafkaSendChannel[A, B, Return] extends SendChannel[A, Return] {
val channelProps: KafkaSendChannelProperties
val kafkaProducer: Producer[String, B]
 override def close(): Unit = kafkaProducer.close()
}

Теперь есть 2 варианта CommanKafkaSendChannel, один с обратным вызовом, а другой с Future:

trait KafkaSendChannelWithFuture[A, B] extends CommonKafkaSendChannel[A, B, Future[RecordMetadata]] {
override def send(data: A): Future[RecordMetadata] = Future {
  kafkaProducer.send(new ProducerRecord[String, B](channelProps.topic)).get
}
}

KafkaSendChannelWithCallback определение:

object KafkaSendChannelWithCallback {

def apply[A, B](oChannelProps: KafkaSendChannelProperties,
              oKafkaProducer: Producer[String, B],
              oCallback: Callback): KafkaSendChannelWithCallback[A, B] =
new KafkaSendChannelWithCallback[A,B] {
  override val channelProps: KafkaSendChannelProperties = oChannelProps
  override val kafkaProducer: Producer[String, B] = oKafkaProducer
  override val callback: Callback = oCallback
}
}

trait KafkaSendChannelWithCallback[A, B] extends CommonKafkaSendChannel[A, B, Unit] {

  val callback: Callback

override def send(data: A): Unit =
kafkaProducer.send(new ProducerRecord[String, B](channelProps.topic), callback)
}

Теперь, основываясь на значении конфигурации, я выбираю правильный тип канала во время выполнения, как показано ниже. Я создаю актера с правильным типом канала, который будет отправлять данные в kafka:

  val sendChannel = kafkaChannel.channel(config, actorSystem).fold(
    error => {
      logger.error("Exception while instantiating the KafkaSendChannel")
      throw error
    },
    success => success
  )

actor = actorSystem.actorOf(IngestionActor.props(config, sendChannel), name = ACTOR_NAME)

Определение актера:

object IngestionRouterActor {
  def props[V](config: Config, sendChannel: SendChannel[V, Unit]): Props =
Props(classOf[IngestionActor[V]], config, sendChannel)
}

Проблема в том, когда я использую KafkaSendChannelWithCallback мой код правильно компилируется, однако, когда я использую KafkaSendChannelWithFuture это дает мне ошибку ниже actor = объявление:

[ошибка]IngestionActor.scala:32: тип шаблона несовместим с ожидаемым типом; [ошибка] найдена: KafkaSendChannelWithFuture[String,V] [ошибка] требуется: SendChannel[V,Unit]

Поскольку оба определения канала расширены от SendChannelэтот код должен был скомпилироваться без ошибок. Я не уверен, почему он не компилируется. Спасибо

1 ответ

Решение

Props за IngestionActor занимает SendChannel[V, Unit], Проходя в KafkaSendChannelWithCallback к этому аргументу работает, потому что это SendChannel[V, Unit],

С другой стороны, KafkaSendChannelWithFuture это SendChannel[V, Future[RecordMetadata]], SendChannel[V, Future[RecordMetadata]] это не SendChannel[V, Unit],

Одним из вариантов является переопределение Props взять SendChannel[V, Any], поскольку Any является супертипом обоих Unit а также Future:

def props[V](config: Config, sendChannel: SendChannel[V, Any]): Props = ???

На данный момент, компилятор все еще недоволен, потому что SendChannel, будучи универсальным типом, по умолчанию инвариантен. Другими словами, ни SendChannel[V, Unit] ни SendChannel[V, Future[RecordMetadata]] имеют тип SendChannel[V, Any],

Чтобы изменить это, сделайте SendChannel ковариантный по второму параметру типа (добавив + напротив B):

trait SendChannel[A, +B] extends CommunicationChannel {
  def send(data: A): B
}
Другие вопросы по тегам