Scala中的模式匹配结构类型

Mit*_*ins 21 scala pattern-matching structural-typing

为什么打印wtf?模式匹配不适用于结构类型吗?

  "hello" match {
    case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
    case _ => println("okie dokie")
  }
Run Code Online (Sandbox Code Playgroud)

Fla*_*gan 19

在Scala解释器中使用未选中的警告on(scala -unchecked)运行此示例会产生以下警告:warning: refinement AnyRef{def doesNotExist(Int,List[_]): Double} in type pattern is unchecked since it is eliminated by erasure.不幸的是,这样的泛型类型在运行时无法检查,因为JVM没有具体的泛型.

JVM在此模式匹配中看到的所有内容是:

"hello" match {
  case s: Object => ... 
  case annon: Object => ...
}
Run Code Online (Sandbox Code Playgroud)

编辑:回应你的评论,我一直在考虑一个解决方案,但没有时间发布它昨天.不幸的是,即使它应该工作,编译器也无法注入正确的Manifest.

您要解决的问题是比较对象是否具有给定的结构类型.这是我一直在考虑的一些代码(Scala 2.8-r20019,因为Scala 2.7.6.final在玩了类似的想法时几次撞到我身上)

type Foo = AnyRef { def doesNotExist(i: Int, x: List[_]): Double }

def getManifest[T](implicit m: Manifest[T]) = m

def isFoo[T](x: T)(implicit mt: Manifest[T]) = 
  mt == getManifest[Foo]
Run Code Online (Sandbox Code Playgroud)

方法isFoo 基本上比较类的清单xFoo.在理想世界中,结构类型的清单应该等于包含所需方法的任何类型的清单.至少那是我的思路.不幸的是,这无法编译,因为编译器在调用时注入Manifest[AnyRef]而不是a .有趣的是,如果您不使用结构类型(例如,),此代码将按预期编译并运行.我将在某个时候发布一个问题,看看为什么这会因结构类型而失败 - 这是一个设计决策,还是只是实验反射API的一个问题.Manifest[Foo]getManifest[Foo]type Foo = String

如果做不到这一点,您可以始终使用Java反射来查看对象是否包含方法.

def containsMethod(x: AnyRef, name: String, params: java.lang.Class[_]*) = {
  try { 
    x.getClass.getMethod(name, params: _*)
    true
    }
  catch {
    case _ =>  false
  }
}
Run Code Online (Sandbox Code Playgroud)

它按预期工作:

containsMethod("foo", "concat", classOf[String]) // true
containsMethod("foo", "bar", classOf[List[Int]]) // false
Run Code Online (Sandbox Code Playgroud)

......但它不是很好.

另请注意,结构类型的结构在运行时不可用.如果你有一个方法def foo(x: {def foo: Int}) = x.foo,在你得到擦除后def foo(x: Object) = [some reflection invoking foo on x],类型信息就会丢失.这就是为什么首先使用反射的原因,因为你必须调用一个方法,Object而JVM不知道是否Object有该方法.


Kim*_*bel 9

如果您将不得不使用反射,您至少可以使用提取器使其看起来更好:

object WithFoo {
    def foo(){
        println("foo was called")
    }
}

object HasFoo {
    def containsMethod(x: AnyRef, name: String, params: Array[java.lang.Class[_]]) : Boolean = {
        try { 
            x.getClass.getMethod(name, params: _*)
            true
        } catch {
            case _ => false
        }
    }

    def unapply(foo:AnyRef):Option[{def foo():Unit}] = {
        if (containsMethod(foo, "foo", new Array[Class[_]](0))) {
            Some(foo.asInstanceOf[{def foo():Unit}])
        } else None
    }
}


WithFoo.asInstanceOf[AnyRef] match {
    case HasFoo(foo) => foo.foo()
    case _ => println("no foo")
}
Run Code Online (Sandbox Code Playgroud)

  • `containsMethod` 可以脱水为 `Try(x.getClass.getMethod(name, params: _*)).isSuccess` (2认同)