Frage

Warum druckt dieser WTF? Funktioniert das Musteranpassung nicht an Strukturarten?

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

Lösung

Ausführen dieses Beispiels im Scala -Dolmetscher mit ungeprüften Warnungen auf (scala -unchecked) erzeugt die folgende Warnung: warning: refinement AnyRef{def doesNotExist(Int,List[_]): Double} in type pattern is unchecked since it is eliminated by erasure. Leider kann ein generischer Typ wie dieser nicht zur Laufzeit überprüft werden, da die JVM keine generischen generika hat.

Alles, was die JVM in diesem Muster -Match sieht, ist:

"hello" match {
  case s: Object => ... 
  case annon: Object => ...
}

BEARBEITEN: Als Antwort auf Ihre Kommentare habe ich über eine Lösung nachgedacht, hatte aber nicht die Zeit, sie gestern zu veröffentlichen. Leider auch wenn es sollte Arbeiten, der Compiler kann das richtige injizieren Manifest.

Das Problem, das Sie lösen möchten, besteht darin, zu vergleichen, ob ein Objekt einen bestimmten Strukturtyp hat. Hier ist ein Code, an den ich gedacht habe (Scala 2.8-R20019, als Scala 2.7.6.Final ein paar Mal bei mir beim Spielen mit ähnlichen Ideen auf mich abgestürzt ist.

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]

Methode isFoo Vergleicht im Grunde die Manifeste der Klasse x von Foo. In einer idealen Welt sollte das Manifest eines Strukturarts dem Manifest von allen Art, die die erforderlichen Methoden enthalten, gleich sein. Zumindest ist das mein Gedankengang. Leider kann dies nicht kompilieren, da der Compiler a injiziert Manifest[AnyRef] anstelle einer Manifest[Foo] beim Anruf getManifest[Foo]. Interessanterweise, wenn Sie keinen Strukturart verwenden (zum Beispiel, type Foo = String), dieser Code kompiliert und funktioniert wie erwartet. Ich werde irgendwann eine Frage stellen, um zu sehen, warum dies bei Strukturarten fehlschlägt - ist es eine Designentscheidung oder es ist nur ein Problem der experimentellen Reflexions -API.

Wenn Sie dies nicht tun, können Sie immer Java Reflection verwenden, um festzustellen, ob ein Objekt eine Methode enthält.

def containsMethod(x: AnyRef, name: String, params: java.lang.Class[_]*) = {
  try { 
    x.getClass.getMethod(name, params: _*)
    true
    }
  catch {
    case _ =>  false
  }
}

was wie erwartet funktioniert:

containsMethod("foo", "concat", classOf[String]) // true
containsMethod("foo", "bar", classOf[List[Int]]) // false

... aber es ist nicht sehr schön.

Beachten Sie auch, dass die Struktur eines Strukturarts zur Laufzeit nicht verfügbar ist. Wenn Sie eine Methode haben def foo(x: {def foo: Int}) = x.foo, nach dem Löschen bekommst du def foo(x: Object) = [some reflection invoking foo on x], Die Typinformationen gehen verloren. Deshalb wird Reflexion überhaupt verwendet, da Sie eine Methode auf eine aufrufen müssen Object und die JVM weiß nicht, ob die Object hat diese Methode.

Andere Tipps

Wenn Sie Reflexion verwenden müssen, können Sie es zumindest mit einem Extraktor schöner aussehen:

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")
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top