如何在Scala中的函数签名中匹配类型成员?

时间:2018-04-17 16:30:40

标签: scala

由于我使用涉及宏和序列化的特定库导致的模糊原因,我选择尝试使用类型成员而不是类的参数(background:Is there a way to use ClassTags with the Autowire library in Scala?

sealed trait Command {
  type M <: CommandMetaData
  val meta: M
}

final case class SysCmdMetaData(
// ...
) extends CommandMetaData


// How to guarantee cmd.M <: SysCmdMetaData
def runInSystem(cmd: Command){
  //...
}

有没有办法静态地这样做?我认为使用类型类和上下文边界是一种选择,但我觉得它有点沉重,因为我已经在这里使用了子类。也许更好的选择只是使元数据成为特征,并更充分地拥抱面向对象,并根据混合的特征创建Command的各种变体。

1 个答案:

答案 0 :(得分:2)

您可以使用精炼类型定义函数参数:

def runInSystem(cmd: Command { type M <: SysCmdMetaData }) = { }

请注意,与类型参数不同,type members are not inferred by default。以下代码将失败:

scala> val cmd: Command = new Command {
  type M = SysCmdMetaData
  val meta = SysCmdMetaData()
}

cmd: Command = $anon$1@830b8a5

scala> runInSystem(cmd)
<console>:15: error: type mismatch;
 found   : cmd.type (with underlying type Command)
 required: Command{type M <: SysCmdMetaData}
       runInSystem(cmd)
                   ^

您必须:

  • 使用Command的某个命名子类,其中M被静态固定为适当的类型。
  • 将匿名子类实例直接传递给runInSystem

    runInSystem(new Command {
      type M = SysCmdMetaData
      val meta = SysCmdMetaData()
    })
    
  • 手动指定传递给runInSystem的变量的类型成员:

    val cmd: Command { type M = SysCmdMetaData } = new Command {
      type M = SysCmdMetaData
      val meta = SysCmdMetaData()
    }
    
    runInSystem(cmd)
    
  • 您还可以使用类型参数定义类型别名:

    object Command {
      type Aux[M0 <: CommandMetaData] = Command { type M = M0 }
    }
    
    val cmd: Command.Aux[SysCmdMetaData] = ???
    
    runInSystem(cmd)