Scala中的图案匹配结构类型
-
22-09-2019 - |
题
为什么打印WTF?模式匹配不适用于结构类型吗?
"hello" match {
case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
case _ => println("okie dokie")
}
解决方案
在Scala解释器中运行此示例,并在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 => ...
}
编辑: 为了回应您的评论,我一直在考虑解决方案,但没有时间昨天发布它。不幸的是,即使 应该 工作,编译器未能注入适当的 Manifest
.
您要解决的问题是比较一个给定的结构类型的对象。这是我一直在想的一些代码(Scala 2.8-R20019,因为Scala 2.7.6.最终在我播放类似想法时撞到了我几次)
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]
方法 isFoo
基本上比较了班级的表现 x
的 Foo
. 。在理想的世界中,结构类型的清单应等于任何包含所需方法的任何类型的表现。至少那是我的思路。不幸的是,由于编译器注入A Manifest[AnyRef]
代替 Manifest[Foo]
打电话时 getManifest[Foo]
. 。有趣的是,如果您不使用结构类型(例如, type Foo = String
),该代码按预期进行编译和工作。我将在某个时候发布一个问题,以了解为什么这会失败结构类型 - 是设计决定,还是只是实验反射API的问题。
如果不这样做,您始终可以使用Java反射来查看对象是否包含方法。
def containsMethod(x: AnyRef, name: String, params: java.lang.Class[_]*) = {
try {
x.getClass.getMethod(name, params: _*)
true
}
catch {
case _ => false
}
}
哪个按预期工作:
containsMethod("foo", "concat", classOf[String]) // true
containsMethod("foo", "bar", classOf[List[Int]]) // false
...但这不是很好。
另外,请注意,结构类型的结构在运行时不可用。如果您有一种方法 def foo(x: {def foo: Int}) = x.foo
, ,擦除后你得到 def foo(x: Object) = [some reflection invoking foo on x]
, ,丢失的类型信息。这就是为什么首先使用反射的原因,因为您必须在 Object
JVM不知道是否 Object
有那种方法。
其他提示
如果您必须使用反射,则至少可以通过提取器使其看起来更好:
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")
}