Vous devez fournir un ClassTag
, pas un TypeTag
, et utiliser la correspondance de motif. ClassTags
fonctionnent bien avec la correspondance de modèle. Vous pouvez même utiliser la méthode collect
pour effectuer la filter
et map
ensemble:
def filter[T, T2](data: Traversable[T2])(implicit ev: ClassTag[T]) = data collect {
case t: T => t
}
Par exemple:
val data = Seq(new B, new B, new C, new B)
filter[B, A](data) //Traversable[B] with length 3
filter[C, A](data) //Traversable[C] with length 1
Un problème est qu'il pourrait ne pas fonctionner comme prévu avec les types génériques imbriqués.
La méthode collect
prend en paramètre un paramètre de type PartialFunction
, représentant une fonction qui n'a pas besoin d'être définie sur l'ensemble du domaine. Lorsque vous utilisez collect
éléments où le PartialFunction
est indéfini sont filtrés, et les éléments qui correspondent à une instruction case
sont mappés en conséquence.
Vous pouvez également utiliser existential types et laisser le compilateur déduire le type du paramètre data
pour une syntaxe plus concise. Vous pouvez également utiliser context bounds:
def filter[T : ClassTag](data: Traversable[_]) = data collect { case t: T => t }
filter[B](data)
Un problème avec les méthodes ici est qu'il ya une différence significative entre la méthode filter
native vous avez: ces méthodes renvoie toujours un Traversable
alors que le natif filter
retourne le meilleur type il peut . Par exemple:
val data = Vector(new B, new B, new C, new B)
data filter { _.isInstanceOf[B] } //Vector[A]
data filter { _.isInstanceOf[B] } map { _.asInstanceOf[B] } //Vector[B]
data collect { case t: B => t } //Vector[B]. Note that if you know the type at the calling point, this is pretty concise and might not need a helper method at all
//As opposed to:
filter[B](data) //Traversable[B], not a Vector!
Vous pouvez résoudre ce problème en utilisant le modèle CanBuildFrom
en utilisant un autre paramètre implicite. Vous pouvez également utiliser implicit classes pour ajouter essentiellement la méthode à la classe (par opposition à l'appel de la méthode dans le style statique indiqué ci-dessus).Tout cela ajoute à une méthode assez compliqué, mais je vais le laisser ici si vous êtes intéressé par ces améliorations:
implicit class RichTraversable[T2, Repr <: TraversableLike[T2, Repr], That](val trav: TraversableLike[T2, Repr]) extends AnyVal {
def withType[T : ClassTag](implicit bf: CanBuildFrom[Repr, T, That]) = trav.collect {
case t: T => t
}
}
Cela vous permettra de le faire:
data.withType[B] //Vector[B], as desired
Salut @BenReich, Merci pour votre belle réponse. Pourriez-vous nous en dire plus sur la syntaxe '{case t: B => t}' de 'data collect {cas t: B => t}'? Pourquoi n'avez-vous pas besoin de fournir une correspondance exhaustive? – worldterminator
@worldterminator J'ai ajouté un petit edit expliquant 'collect' un peu plus. –
@BenReich Merci, le dernier morceau de code est exactement ce dont j'avais besoin. C'est aussi un parfait exemple des possibilités de Scala et de la difficulté de son système de types et de sa bibliothèque de collections. J'espère pouvoir maîtriser des choses comme ça bientôt. – user42723