Question

Pourquoi est-ce wtf d'impression? Est-ce pattern matching fonctionne pas sur les types de structure?

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

La solution

L'exécution de cet exemple dans l'interpréteur Scala avec des avertissements non vérifiées sur (de scala -unchecked) produit l'avertissement suivant: warning: refinement AnyRef{def doesNotExist(Int,List[_]): Double} in type pattern is unchecked since it is eliminated by erasure. Malheureusement, un type générique comme celui-ci ne peut pas être vérifiée lors de l'exécution que la machine virtuelle Java ne dispose pas de génériques réifié.

Tout ce que la machine virtuelle Java voit dans ce match de modèle est:

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

EDIT: En réponse à vos commentaires, j'ai pensé à une solution, mais n'a pas eu le temps de poster hier. Malheureusement, même si elle devrait travail, le compilateur ne parvient pas à injecter la Manifest appropriée.

Le problème que vous voulez résoudre est de comparer si un objet est d'un type de structure donnée. Voici un code que j'ai pensé (Scala 2.8-r20019, comme Scala 2.7.6.final écrasé sur moi deux ou trois fois tout en jouant avec des idées similaires)

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]

Méthode isFoo compare essentiellement les manifestes de la x de classe de Foo. Dans un monde idéal, le manifeste d'un type de structure doit être égale au manifeste de tout type contenant les méthodes nécessaires. Au moins, c'est mon train de pensée. Malheureusement, cela ne peut pas compiler, comme le compilateur injecte un Manifest[AnyRef] au lieu d'un Manifest[Foo] lorsque vous appelez getManifest[Foo]. Il est intéressant, si vous n'utilisez pas un type de structure (par exemple, type Foo = String), ce code compile et fonctionne comme prévu. Je vais poster une question à un moment donné de voir pourquoi cela ne fonctionne pas avec les types de structure -. Est une décision de conception, ou il est juste un problème de l'API de réflexion expérimentale

A défaut, vous pouvez toujours utiliser la réflexion Java pour voir si un objet contient une méthode.

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

qui fonctionne comme prévu:

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

... mais ce n'est pas très agréable.

En outre, notez que la structure d'un type de construction ne sont pas disponibles lors de l'exécution. Si vous avez une méthode def foo(x: {def foo: Int}) = x.foo, après l'effacement vous def foo(x: Object) = [some reflection invoking foo on x], les informations de type étant perdu. C'est la raison pour laquelle la réflexion est utilisé en premier lieu, que vous devez invoquer une méthode sur un Object et la machine virtuelle Java ne sait pas si le Object a cette méthode.

Autres conseils

Si vous allez devoir utiliser la réflexion, vous pouvez au moins le rendre plus joli avec un extracteur:

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")
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top