21

为什么这会打印 wtf?模式匹配对结构类型不起作用吗?

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

2 回答 2

19

在 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具有该方法。

于 2010-01-01T11:43:50.713 回答
10

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

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")
}
于 2010-08-08T15:15:39.037 回答