代码之家  ›  专栏  ›  技术社区  ›  Mitch Blevins

scala中的模式匹配结构类型

  •  21
  • Mitch Blevins  · 技术社区  · 15 年前

    为什么要打印wtf?模式匹配在结构类型上不起作用吗?

      "hello" match {
        case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
        case _ => println("okie dokie")
      }
    
    2 回复  |  直到 8 年前
        1
  •  19
  •   Flaviu Cipcigan    8 年前

    在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,scala2.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 . 在理想情况下,结构类型的清单应该等于包含所需方法的任何类型的清单。至少这是我的思路。不幸的是,由于编译器注入了 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不知道 对象 有那种方法。

        2
  •  9
  •   Kim Stebel    15 年前

    如果你要使用反射,你至少可以用提取器使它看起来更好:

    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")
    }
    
    推荐文章