【问题标题】:Scala pattern matching type with inherited classes具有继承类的 Scala 模式匹配类型
【发布时间】:2020-08-23 05:07:44
【问题描述】:

我有一系列继承的类,比基类有更多的方法。像这样:

class Animal
{  
  var Name: String
}  
class Fish extends Animal
{  
  def swim()  
  {  
    println("I'm a Fish and i'm swimming!");  
  }
}
class Turtle extends Animal
{
  def swim()  
  {  
    println("I'm a Turtle and i'm swimming!");  
  }
}

我想将类型匹配模式用于通用 Animal 类,以识别确切的类型并应用 swim() 方法(如果可以),如下所示:

myAnimal match {
  case m:Fish => m.Swim()
  case m:Turtle => m.Swim()
  case _: => doSomethingElse()
}

我想用优雅的方式来写它,避免连续重复行。

我知道我可以做到:

myAnimal match {
  case (_:Fish | _:Turtle) => println("I can do this!")
}

而且我知道,正如我在上面所写的,我可以做到:

myAnimal match {
  case m:Fish => m.swim()
}

但是,我不能把它们放进去,或者像这样

myAnimal match {
  case (m:Fish | m:Turtle) => m.swim() //ERROR (cannot recognize swim() method)

  //Not even this
  case m @ (_:Fish | _:Turtle) => m.swim() //ERROR (cannot recognize swim() method)
  case _: => doSomethingElse()
}

一个好的解决方案是插入一个中间类,例如扩展AnimalsAnimalsThatCanSwim。这个解决方案应该是最后的选择,因为我必须避免更改扩展类。

【问题讨论】:

    标签: scala types pattern-matching


    【解决方案1】:

    您可以将结构类型与使用反射的提取器结合使用来检查您的对象是否具有swim 方法。感谢 Mateusz Kubuszok 和 Dmytro Mitin,我现在有了一个似乎可行的解决方案。

    这样使用:

    myAnimal match {
      case CanSwim(m) => m.swim()
      case _          => println("Boohoo, I can't swim!")
    }
    

    其他的东西:

    import scala.reflect.runtime.universe._
    
    type CanSwim = { def swim(): Unit }
    
    object CanSwim {
      def unapply(arg: Any): Option[CanSwim] = {
        try {
          var res: Option[CanSwim] = None
          for (symb <- runtimeMirror(arg.getClass.getClassLoader)
                 .reflect(arg)
                 .symbol
                 .info
                 .member(TermName("swim"))  //get all methods named swim
                 .asTerm
                 .alternatives) { //alternatives because it might be overloaded
            if (symb.isMethod) {
              val msymb = symb.asMethod
              //Check if the signature matches (Returns Unit and has 1 empty parameter list)
              if (msymb.returnType =:= typeOf[Unit] && msymb.paramLists == List(List()))
                res = Some(arg.asInstanceOf[CanSwim])
            }
          }
          res
        } catch {
          case _ => None  
          //Might want to change this, but I don't think it's necessary to handle or throw exceptions
          //If it failed, it probably means it can't swim
        }
      }
    }
    

    链接到Scastie

    但是,我真的不推荐它。重构代码可能更容易。

    &lt;script src="https://scastie.scala-lang.org/gFBe7jTQQiW3WnPVTJoFPw.js"&gt;&lt;/script&gt;

    【讨论】:

    • 如果你在不是CanSwim的东西上尝试这种模式匹配,你会得到java.lang.NoSuchMethodException,在一些尝试中运行转换为结构类型会更安全尝试并使用该逻辑来构建一个自定义提取器。
    • @user 现在swim 被执行了两次。
    • @DmytroMitin 是的,感谢您指出这一点,将尝试修复
    猜你喜欢
    • 2016-04-02
    • 2020-08-09
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 2014-03-05
    • 1970-01-01
    • 2019-03-29
    • 2019-04-20
    相关资源
    最近更新 更多