Вы пытаетесь реализовать концепцию частичной функции. При этом вам нужно знать, что вы жертвуете безопасностью типа для динамичности. На строго типизированном языке вы обычно не можете делать этого без какого-либо явного взлома, например. смягчение правил приведения типов или дисперсий.
Например, стандартный номер Scala PartialFunction
делает это для вас: он позволяет назначить функцию другой функции, которая отключает проверку контравариантности параметра. В качестве примера, имеющей иерархию
trait Animal
case class Dog() extends Animal
case class Cat() extends Animal
типа, это позволяет «назначать» функцию типа Dog => T
в зависимости от типа Animal => T
(что противоречит правилам контрвариации):
val pf: PartialFunction[Animal, Unit] = {
case Dog() => println("dog")
}
За счет на данный момент возможны ошибки совпадения во время выполнения:
pf(Cat()) // MatchError
Таким образом, в целом, чтобы объединить несколько потребителей в Scala вы можете сделать точно такой же трюк Scala язык делает для вас при определении частичных функций - изменение дисперсии потребительских параметров до ковариантного, то есть -T
до +T
. Чтобы сделать это, используйте @uncheckedVariance
аннотацию:
trait Consumer[+T, +V] extends ([email protected] ⇒ V) {
def handle(command: [email protected]): V
}
Такое отклонение позволяет иметь таблицу поиска потребителей:
val lookup: Map[Class[_], Consumer[Animal]] = Map(
classOf[Dog] → dogConsumer,
classOf[Cat] → catConsumer
)
, которые могут быть использованы для реализации отправки на основе (в данном примере случае) тип класса ,