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 => ...
}

編集:あなたのコメントに応えて、解決策を考えていましたが、昨日投稿する時間がありませんでした. 残念ながら、それが機能するはずであっても、コンパイラは適切な .xml を挿入できません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。理想的な世界では、構造型のマニフェストは、必要なメソッドを含む任意の型のマニフェストと同じである必要があります。少なくともそれが私の考えです。残念ながら、コンパイラはを呼び出すときにa の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
  }
}

期待どおりに動作します:

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はそのためです。

于 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 に答える