Scala:如何模式匹配内部案例类的封闭对象?

时间:2015-02-10 10:36:23

标签: scala pattern-matching case-class

我有一个内部案例类,特别是来自this问题的事件,并希望匹配它,包括外部对象:

class Player {
  var _life = 20
  def life = _life

  def gainLife(life: Int) = execute(GainLife(life))

  case class GainLife(life: Int) extends Event {
    def execute() = _life += life
  }
}

我可以轻松编写替换特定玩家生活事件的效果(部分功能):

//gain twice as much life
def effect(player: Player): ReplacementEffect = {
  case player.GainLife(x) => player.GainLife(x * 2)
}
但是,我不能为其他玩家做同样的事情。我最接近的是:

//only you gain life
def effect2(player: Player): ReplacementEffect = {
  case evt: Player#GainLife => player.GainLife(evt.life)
}

但是1)这甚至取代了你自己的生活费用新的生活费用,2)我无法引用最初在该功能中获得生命的玩家3)我错过了直接匹配{{ 1}}这样。

这可以使用与路径无关的类型表示,如

life

理想情况下,我想要像

这样的东西
case Player.GainLife(_player, life) if _player != player => GainLife(player, life)

这可能是某种方式,还是我可以解决这个问题?或者我是否必须使用GainLife嵌套?

2 个答案:

答案 0 :(得分:5)

当你在另一个类中定义类时,这意味着该类型特定于周围的类,因此,如果你想让它表示,则playerA.GainLife与playerB.GainLife的类型不同(这被称为路径依赖类型)无论实例如何,您都在相同的范围内定义它:同一个包或您的类的伴随对象。

您可以在此问题中阅读更多内容:What is meant by Scala's path-dependent types?

答案 1 :(得分:0)

我最接近的是定义我自己的unapply方法:

class Player {
  self =>

  var _life = 20
  def life = _life

  def gainLife(life: Int) = execute(GainLife(life))

  case class GainLife(life: Int) extends Event {
    def player = self

    def execute() = _life += life
  }
}

object Player {
  object _GainLife {
    def unapply(event: Player#GainLife) =
      Some((event.player, event.life))
  }
}

请注意,命名Player._GainLife对象Player.GainLife会导致名称冲突,这是最重要的缺点。因此,我选择从Player命名空间外部提供该类型:

val GainLife = Player._GainLife

这允许使用player.GainLife.unapply和Player._GainLife.unapply以简洁的方式匹配:

//gain twice as much life
def effect1(player: Player): ReplacementEffect = {
  case player.GainLife(life) => player.GainLife(life * 2)
}

//only you gain life
def effect2(player: Player): ReplacementEffect = {
  case GainLife(_player, life) if _player != player => player.GainLife(life)
}

//all players gain twice as much life
def effect3: ReplacementEffect = {
  case GainLife(player, life) => player.GainLife(life * 2)
}

最后两个示例看起来有点不对称,但如果需要,可以使用apply方法修复。