2010-09-07 7 views
4

im nouveau à scala et a couru dans le problème suivant:filtre collection Scala par type

Je veux obtenir une sous-collection d'une collection existante qui ne contient que des éléments d'un type particulier. Les travaux suivants:

class C(val name : String) 
class D(name : String) extends C(name) { } 

val collection = Set[C](new C("C1"),new D("D1"),new C("C2"),new D("D2")) 
collection.collect{case d : D => d}.size must be === 2 // works 

Mais lorsque je tente d'étendre les classes de collection avec une méthode « onlyInstancesOf [type] » cela ne fonctionne pas. Tout d'abord ma mise en œuvre:

object Collection { 
    implicit def extendScalaCollection[E](coll : Traversable[E]) = new CollectionExtension[E](coll) 
} 

class CollectionExtension[E](coll : Traversable[E]) { 

    def onlyInstancesOf[SpecialE <: E] : Traversable[SpecialE] = { 
     coll.collect({case special : SpecialE => special}).asInstanceOf[Traversable[SpecialE]] 
    } 
} 

Alors quand j'utiliser cette extension et Execute:

collection.onlyInstancesOf[D].size must be === 2 

Je reçois une erreur qui .Size retourné 4 et non 2. En outre, j'ai vérifié, le résultat contient effectivement C1 et C2 bien que cela ne devrait pas.

Quand je fais:

collection.onlyInstancesOf[D].foreach(e => println(e.name)) 

je reçois l'exception:

java.lang.ClassCastException: CollectionsSpec$$anonfun$1$C$1 cannot be cast to CollectionsSpec$$anonfun$1$D$1 

Alors, évidemment, l'ensemble contient encore obtenu les éléments qui auraient dû être filtrés.

Je ne comprends pas pourquoi cela arrive, quelqu'un peut-il expliquer cela?

Edit: Scala: Code Scala coureur Version 2.8.0.final

+2

Ce code ne génère-t-il pas d'avertissement? – mkneissl

+0

Vous avez raison, mais comme il s'est avéré que l'avertissement n'a pas été affiché pendant le test car le fichier a été compilé il y a quelque temps et n'a pas été recompilé:/ – Ragmaanir

+0

duplication possible de [type de collection Scala pour filtre] (http: // stackoverflow. com/questions/2218558/scala-collection-type-pour-filtre) – thSoft

Répondre

10

Faites attention aux avertissements du compilateur, et ajoutez -unchecked vos options de ligne de commande scala.

M:\>scala -unchecked 
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) Client VM, Java 1.6.0_21) 
. 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> class CollectionExtension[E](coll : Traversable[E]) { 
    | 
    |  def onlyInstancesOf[SpecialE <: E] : Traversable[SpecialE] = { 
    |   coll.collect({case special : SpecialE => special}).asInstanceOf[Traversable[SpecialE]] 
    |  } 
    | } 
<console>:8: warning: abstract type SpecialE in type pattern SpecialE is unchecked since it is eliminated by erasure 
       coll.collect({case special : SpecialE => special}).asInstanceOf[Traversable[SpecialE]] 
              ^
defined class CollectionExtension 

L'avertissement signifie que le meilleur compilateur peut faire est équivalent à:

coll.collect({case special : AnyRef => special}).asInstanceOf[Traversable[_]] 

Pour une explication plus détaillée de l'effacement de type, et les moyens que vous pouvez travailler autour d'elle avec Manifestes, voir:

https://stackoverflow.com/questions/tagged/type-erasure+scala

4

Scala fonctionne sur la machine virtuelle Java, ce qui efface malheureusement paramètres de type lors de l'exécution: http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure. Dans votre premier exemple, vous donnez le type dans une position non effacée et le code d'exécution peut faire la comparaison. Dans le deuxième exemple, le type SpecialE est effacé et le code renverra tout.

Vous pouvez utiliser Manifestes de scala pour retrouver une partie de l'information perdue par l'effacement de type:

import scala.reflect.ClassManifest 
class CollectionsExtension[E <: AnyRef](coll : Traversable[E]) { 
    def onlyInstancesOf[SpecialE <: E](implicit m : Manifest[SpecialE]) : Traversable[SpecialE] = { 
    coll.collect({case e if (ClassManifest.singleType(e) <:< m) => e}).asInstanceOf[Traversable[SpecialE]] 
    } 
} 
3

Comme l'avertissement disent:

<console>:14: warning: abstract type SpecialE in type pattern SpecialE is unchecked since it is eliminated by erasure 
       coll.collect({case special : SpecialE => special}).asInstanceOf[Traversable[SpecialE]] 

Voyons voir la mise en œuvre de collect:

def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { 
    val b = bf(repr) 
    for (x <- this) if (pf.isDefinedAt(x)) b += pf(x) 
    b.result 
} 

Notez qu'il n'y a pas de correspondance de formes ici. C'est la différence fondamentale - quand vous écrivez "collection.collect{case d : D => d}" le compilateur sait exactement de quel type vous parlez: D.D'autre part, lorsque vous écrivez coll.collect({case special : SpecialE => special}), le compilateur ne sait pas de quel type est SpecialE, car SpecialE est juste un paramètre de type. Donc, il ne peut pas générer de code qui sait ce qu'est SpecialE, et, au moment de l'exécution, il n'y a plus de SpecialE - le bytecode utilise simplement java.lang.Object.

6

Comme d'autres l'ont souligné, les manifestes peuvent vous sauver. Voici un exemple de la façon dont, nous limitant aux non-primitives, et en supposant que nous ne voulons pas stocker manifeste dans nos collections, mais plutôt utiliser la réflexion sur place pour comprendre les choses:

class CollectionExtension[E <: AnyRef](coll : Traversable[E]) { 
    def onlyInstancesOf[SpecialE <: E](implicit mf : Manifest[SpecialE]) : Traversable[SpecialE] = { 
    coll.collect({ 
     case special if mf.erasure.isAssignableFrom(special.getClass) => special 
    }).asInstanceOf[Traversable[SpecialE]] 
    } 
} 

et ici il est action:

scala> val ce = new CollectionExtension(List(Some(1),Some(5),"This","Fox")) 
ce: CollectionExtension[java.lang.Object] = [email protected] 

scala> val opts = ce.onlyInstancesOf[Some[_]] 
opts: Traversable[Some[_]] = List(Some(1), Some(5)) 

scala> val strings = ce.onlyInstancesOf[String] 
strings: Traversable[String] = List(This, Fox) 
Questions connexes