Scala:未绑定的通配符类型

时间:2017-10-15 03:07:18

标签: scala generics

我有以下代码

import scala.collection.mutable
import scala.reflect.ClassTag
import scala.reflect._

object EventManager {

  var handlers: Map[Class[_ <: Event], mutable.Set[EventHandler[_ <: Event]]] = Map()

  def registerHandler [A <: Event](handler: EventHandler[A])(implicit tag: ClassTag[A]): Unit = {

    val typeParam = tag.runtimeClass.asSubclass(classOf[Event])

    handlers.get(tag.runtimeClass.asSubclass(classOf[Event])) match {
      case Some(_) => handlers(typeParam) += handler.asInstanceOf[EventHandler[Event]]
      case None => handlers += (typeParam -> mutable.Set(handler))
    }
  }

  //Returns true if the event was cancelled, false otherwise
  def fireEvent[A <: Event](event: A)(implicit tag: ClassTag[A]): Boolean = {

    val typeParam = tag.runtimeClass.asSubclass(classOf[Event])

    event match {
      case cEvent: CancellableEvent =>

        handlers.get(typeParam) match {
          case Some(s) =>
            s.foreach(handler => if (!cEvent.cancelled) handler.handle(event.asInstanceOf[_ <: Event]))
          case None =>
        }
        //Return if the event was cancelled or not
        cEvent.cancelled

      case _ =>
        handlers.get(typeParam) match {
          case Some(s) => s.foreach(_.handle(event.asInstanceOf[_ <: Event]))
          case None =>
        }
        false
    }
  }
}

trait Event

trait EventHandler[A <: Event]{
  def handle(event: A)
}

trait CancellableEvent extends Event{
  var cancelled: Boolean = false
}

它的目的是成为一个简单的事件处理系统,虽然我无法弄清楚如何解决这个令人讨厌的错误。

Error:(31, 91) unbound wildcard type
        s.foreach(handler => if (!cEvent.cancelled) handler.handle(event.asInstanceOf[_ <: Event]))

老实说,我不知道我能做些什么来解决这个问题。此外,我还没有与Scala合作,所以如果你有任何关于如何使我的代码更加惯用的建议,请随意!

1 个答案:

答案 0 :(得分:1)

我可以看到编译代码的最短路径是:

-            s.foreach(handler => if (!cEvent.cancelled) handler.handle(event.asInstanceOf[_ <: Event]))
+            s.foreach{case handler: EventHandler[t] =>
+              if (!cEvent.cancelled) handler.handle(event.asInstanceOf[t])}

-          case Some(s) => s.foreach(_.handle(event.asInstanceOf[_ <: Event]))
+          case Some(s) =>
+            s.foreach{case handler: EventHandler[t] =>
+              handler.handle(event.asInstanceOf[t])}

但是,我想知道为什么你甚至根本不打扰EventHandler上的类型参数。你使用它的方式在编译时没有安全性。类型参数总是存在的,因此编译器不知道实际类型是什么,所以编译器可以做的就是拒绝注销,因为它没有足够的信息来确保代码是安全的,强制你覆盖它与asInstanceOf

我认为你应该选择前进的两条路径之一:

  1. 尝试使用正确的编译时检查实现一个实际类型安全的解决方案,避开asInstanceOf逃逸舱口。不要使用scala.reflect,不要使用java.lang.Class,不要使用ClassTag - 所有这些都可能在运行时失败。
  2. 或者,完全放弃类型安全,并在运行时做出正确的事情。你不会得到你想要的安全性,但你的代码会简单得多。
  3. 你现在所拥有的东西对我来说似乎是一种混乱 - Java反射,Scala反射,类型参数,模式匹配,所有这些都似乎不是连贯思考的产物混乱你希望在编译时和运行时发生什么。缺乏连贯性使得即使不写出比这更长的答案也难以批评。