【问题标题】:How to match on type member in function signature in Scala?如何匹配Scala函数签名中的类型成员?
【发布时间】:2018-04-18 02:26:15
【问题描述】:

由于我正在使用的特定库涉及宏和序列化,出于模糊的原因,我选择尝试使用类型成员而不是类的类型参数(背景: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){
  //...
}

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

【问题讨论】:

    标签: scala


    【解决方案1】:

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

    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)
      

    【讨论】:

    • 我选择了您的第一个建议(因为我已经有了子类浮动),但是您列出了这么多替代方案真是太好了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多