Scala中F-界多态与继承结合的事件系统实现问题
解决方案:Scala事件类型系统的继承适配问题
问题根源
原F-界多态实现中,父类Thing继承Eventable[Thing]后,子类Gadget的Eventable类型参数被固定为Thing,导致无法订阅Handler[Gadget];同时Handler的逆变特性使得子类专属处理器无法适配父类类型的订阅方法。
方案一:使用抽象类型成员替代F-界类型参数
通过抽象类型成员允许子类重写匹配自身类型,解决继承时的类型固定问题,同时兼容Handler的逆变特性。
修改后的核心代码
import scala.collection.mutable.{ArrayBuffer, HashMap} class Handler[-A](val name: String = null)(val fn: A => Unit) { def apply(obj: A): Unit = fn(obj) } trait Eventable { type Self <: Eventable this: Self => private val subscribers = HashMap[String, ArrayBuffer[Handler[Self]]]() def subscribe(event: String, handler: Handler[Self]): Unit = { subscribers.getOrElseUpdate(event, ArrayBuffer()).append(handler) } def fireEvent(event: String): Unit = { subscribers.get(event).foreach { subs => subs.foreach(_(this)) } } } object Eventable { def fireAll(event: String, eventables: Seq[Eventable]): Unit = { eventables.foreach(_.fireEvent(event)) } }
业务类定义
class Thing(val name: String) extends Eventable { override type Self = Thing } class Gadget(name: String, val temp: Int) extends Thing(name) { override type Self = Gadget }
测试验证
val thing = new Thing("Item") val gadget = new Gadget("Gadget", 85) val handler1 = new Handler[Thing]()(a => println(a.name)) val handler2 = new Handler[Object]()(a => println(a.hashCode())) val handler4 = new Handler[Gadget]("GadgetHandler")(a => println(s"${a.name} [${a.temp}]")) // Thing可订阅自身及父类型处理器 thing.subscribe("evt", handler1) thing.subscribe("evt", handler2) // Gadget可订阅自身、父类型Thing及上层Object处理器 gadget.subscribe("evt", handler4) gadget.subscribe("evt", handler1) gadget.subscribe("evt", handler2) // 触发事件 thing.fireEvent("evt") // 输出: // Item // [Thing实例的hashCode] gadget.fireEvent("evt") // 输出: // Gadget [85] // Gadget // [Gadget实例的hashCode]
方案优势
- 抽象类型成员
Self允许子类重写为自身类型,打破F-界多态的类型固定限制; - 利用
Handler的逆变特性,父类型处理器(如Handler[Thing])可自动适配子类的订阅方法,满足“对象可绑定自身或父类型订阅者”的需求; - 触发事件时,
this的类型与子类Self匹配,能正确调用所有订阅的处理器。
方案二:使用类型类实现Eventable(无侵入式)
若不想修改现有类的继承结构,可采用类型类方案,无需混入trait即可让任意类支持事件系统。
核心代码
import scala.collection.mutable.{ArrayBuffer, HashMap} class Handler[-A](val name: String = null)(val fn: A => Unit) { def apply(obj: A): Unit = fn(obj) } trait Eventable[A] { def subscribe(a: A, event: String, handler: Handler[A]): Unit def fireEvent(a: A, event: String): Unit } object Eventable { private val subscribers = HashMap[String, HashMap[AnyRef, ArrayBuffer[Handler[_]]]]() implicit def eventable[A]: Eventable[A] = new Eventable[A] { override def subscribe(a: A, event: String, handler: Handler[A]): Unit = { val eventSubs = subscribers.getOrElseUpdate(event, HashMap()) val subs = eventSubs.getOrElseUpdate(a.asInstanceOf[AnyRef], ArrayBuffer()) subs.append(handler) } override def fireEvent(a: A, event: String): Unit = { subscribers.get(event) .flatMap(_.get(a.asInstanceOf[AnyRef])) .foreach { subs => subs.foreach(_.asInstanceOf[Handler[A]].apply(a)) } } } def fireAll[A](event: String, eventables: Seq[A])(implicit ev: Eventable[A]): Unit = { eventables.foreach(ev.fireEvent(_, event)) } }
使用方式
class Thing(val name: String) class Gadget(name: String, val temp: Int) extends Thing(name) val thing = new Thing("Item") val gadget = new Gadget("Gadget", 85) val handler1 = new Handler[Thing]()(a => println(a.name)) val handler2 = new Handler[Object]()(a => println(a.hashCode())) val handler4 = new Handler[Gadget]("GadgetHandler")(a => println(s"${a.name} [${a.temp}]")) Eventable[Thing].subscribe(thing, "evt", handler1) Eventable[Thing].subscribe(thing, "evt", handler2) Eventable[Gadget].subscribe(gadget, "evt", handler4) Eventable[Gadget].subscribe(gadget, "evt", handler1) Eventable[Gadget].subscribe(gadget, "evt", handler2) Eventable[Thing].fireEvent(thing, "evt") Eventable[Gadget].fireEvent(gadget, "evt")
方案优势
- 无需修改现有类的继承体系,通过隐式实例实现无侵入式扩展;
- 灵活性更高,任意类都可快速接入事件系统。
内容的提问来源于stack exchange,提问作者art-solopov
相关产品推荐
相关产品推荐

