具有扩展类

时间:2016-10-23 19:50:46

标签: scala

我正在尝试使用一个使用泛型类型的函数,该函数作为映射定义的一部分传递。

这是我的代码

object EventMapping {


  val eventMapping =  collection.mutable.Map[Class[_ <: EventBase], (User, EventBase) => Unit]()

    setMapping(classOf[UserCreated], (user, evt) => user.loadUserName(evt.userName))


   private def setMapping[T <: EventBase](clazz: Class[T],fn: (User, T) => Unit) {
    eventMapping += clazz -> fn.asInstanceOf[(User, EventBase) => Unit]
  }

}

UserCreated类是一个从EventBase扩展的en事件类,因为在setMapping中我将T定义为从EventBase扩展,并且在setMapping调用中我定义了我在函数中使用的类类型

user.loadUserName(evt.userName))

我原以为它会被视为CreatedUser事件,但编译器仍将其视为EventBase。

基于Java的类似代码可以工作,但我不知道我在这里缺少什么。

这里是UserCreated类

class UserCreated @JsonCreator() (@JsonProperty("userName")val userName: String) extends EventBase{


  @JsonProperty("userName") def getUserName: String = {
    userName
  }
}

这是堆栈跟踪

[info] Compiling 5 Scala sources to /Development/proyectX/target/scala-2.11/classes...
[error] /Development/proyectX/app/persistance/EventMapping.scala:11: missing parameter type
[error]   setMapping(classOf[UserCreated], (user, evt) => user.loadUserName(evt.asInstanceOf[UserCreated].userName))
[error]                                           ^
[error] one error found
[error] (compile:compile) Compilation failed

1 个答案:

答案 0 :(得分:3)

问题是,在setMapping的定义中,您只告诉编译器TEventBase的子类型。因此,当您致电setMapping并在通话中evt.userName时,编译器无法保证EventBase的所有子类型都支持userName成员,因此&# 39;编译错误。因此,当您执行evt.asInstanceOf[UserCreated].userName时,您个人向编译器保证evt实际上是UserCreated并且它确实支持userName成员。

其次,missing parameter type编译错误(不是堆栈跟踪btw,堆栈跟踪仅来自运行时异常)是Scala的类型推理算法不完美的结果。由于算法中的怪癖,您可以通过将映射函数(fn)移动到其自己的参数列表中来使其更准确:

object EventMapping {
  val eventMapping =
    collection.mutable.Map[Class[_ <: EventBase], (User, EventBase) => Unit]()

  setMapping(classOf[UserCreated]) { (user, evt) =>
    user.loadUserName(evt.userName)
  }

  private def setMapping[T <: EventBase](
    clazz: Class[T])(fn: (User, T) => Unit): Unit =
    eventMapping += clazz -> fn.asInstanceOf[(User, EventBase) => Unit]
}

这也将消除对低估{{​​1}}的需要,因为现在编译器可以正确推断它。

最后,有时类型推断器无法正确排列所有内容并仍然会出现编译错误。在这种情况下,您可以只显式传入一个类型参数:

evt.asInstanceOf[UserCreated]

这告诉编译器,无论你在哪个地方都有通用类型setMapping[UserCreated](classOf[UserCreated]) { (user, evt) => user.loadUserName(evt.userName) } ,在这个调用中用T替换它。

P.S。你必须向下转换的事实通常表明你可以使用更具惯用性和可编写性的Scala功能 - 类型类。