You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 05:44:54