为什么这会打印 wtf?模式匹配对结构类型不起作用吗?
"hello" match {
case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
case _ => println("okie dokie")
}
为什么这会打印 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.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]
方法isFoo
基本上是比较类的x
清单Foo
。在理想世界中,结构类型的清单应该等于包含所需方法的任何类型的清单。至少这是我的思路。不幸的是,这无法编译,因为编译器在调用时注入了 aManifest[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
}
}
按预期工作:
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]
,类型信息就丢失了。这就是首先使用反射的原因,因为您必须在 an 上调用一个方法,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")
}