Несоответствие типов черт 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
}