2010-02-13 7 views
5

Je souhaite une carte qui essaie de remplacer une valeur pour une clé existante. J'ai essayé:Extension d'une collection Scala

trait Unoverwriteable[A, B] extends scala.collection.Map[A, B] { 
    case class KeyAlreadyExistsException(e: String) extends Exception(e) 

    abstract override def + [B1 >: B] (kv: (A, B1)): Unoverwriteable[A, B1] = { 
     if (this contains(kv _1)) throw new KeyAlreadyExistsException(
      "key already exists in WritableOnce map: %s".format((kv _1) toString) 
     ) 
     super.+(kv) 
    } 

    abstract override def get(key: A): Option[B] = super.get(key) 
    abstract override def iterator: Iterator[(A, B)] = super.iterator 
    abstract override def -(key: A): Unoverwriteable[A, B] = super.-(key) 
} 

et a obtenu:

<console>:11: error: type mismatch; 
found : scala.collection.Map[A,B1] 
required: Unoverwirteable[A,B1] 
       super.+(kv) 
        ^
<console>:16: error: type mismatch; 
found : scala.collection.Map[A,B] 
required: Unoverwirteable[A,B] 
      abstract override def -(key: A): Unoverwirteable[A, B] = super.-(key) 
                     ^

Je suis tout à fait nouveau pour Scala et ne peut pas trouver un moyen de surmonter cela. De l'aide? :)

edit: J'utilise Scala 2.8.0.Beta1-préversion (qui apporte quelques modifications à scala.collection)

Répondre

4

Comme vous Redéfinition des méthodes de Map, vous ne pouvez pas définir votre trait comme le type de retour.

La solution la plus simple est de simplement omettre les types:

abstract override def + [B1 >: B] (kv: (A, B1)) = { /* ... */ } 
// ... 
abstract override def -(key: A) = super.-(key) 

Ou vous pourriez être explicite et ajouter le type super:

import scala.collection.Map 
abstract override def +[B1 >: B] (kv: (A, B1)): Map[A, B1] = { /* ... */ } 
// ... 
abstract override def -(key: A) = super.-(key): Map[A, B] 

Je pense que vous ne devez remplacer + cependant, comme vos autres méthodes déléguer uniquement à Map.

4

Ce fixe votre erreur de compilation:

trait Unoverwriteable[A, B] extends scala.collection.Map[A, B] { 
    case class KeyAlreadyExistsException(e: String) extends Exception(e) 

    abstract override def + [B1 >: B] (kv: (A, B1)): scala.collection.Map[A, B1] = { 
     if (this contains(kv _1)) throw new KeyAlreadyExistsException(
      "key already exists in WritableOnce map: %s".format((kv _1) toString) 
     ) 
     super.+[B1](kv) 
    } 

    abstract override def get(key: A): Option[B] = super.get(key) 
    abstract override def iterator: Iterator[(A, B)] = super.iterator 
    abstract override def -(key: A): scala.collection.Map[A, B] = super.-(key) 
} 

Cependant, je pense que vous voulez vraiment décorer le collection.mutable.Map#+=, comme suit:

trait Unoverwriteable[A, B] extends collection.mutable.Map[A, B] { 
    case class KeyAlreadyExistsException(e: String) extends Exception(e) 

    abstract override def +=(kv: (A, B)): this.type = { 
    if (this contains (kv _1)) 
     throw new KeyAlreadyExistsException("key already exists in WritableOnce map: %s".format((kv _1) toString)) 
    super.+=(kv) 
    } 
} 
+2

Dans le cas où on ne sait pas pourquoi vous voulez étendre 'collection.mutable.Map', c'est parce que lorsque vous étendez une carte immuable, chaque appel à' '+ vous donne un map_ de _NEW. Puisque vous créez la nouvelle carte avec un appel à super, cette nouvelle carte ne sera pas inscriptible! Il y a deux solutions: remplacer tout ce qui n'est pas par des appels à super mais avec vos propres routines qui prennent une ancienne invendable immutable et en créer une nouvelle avec le nouvel élément (si autorisé); ou, utilisez une carte modifiable et continuez d'ajouter à la même carte au lieu de la remplacer. Ce dernier est beaucoup moins de travail. –

3

Vous pouvez le faire en utilisant un scala.collection.immutable .Map avec un peu de magie implicite. Autrement dit, vous définissez une méthode supplémentaire dans l'interface et une conversion implicite. Voici comment je le ferais en 2.7, je suis sûr qu'il y a différentes méthodes pour surcharger en 2.8, mais vous devriez avoir l'idée générale.

trait Unoverwriteable[A, B] extends scala.collection.immutable.Map[A, B] { 
    import Unoverwriteable.unoverwriteableMap 

    case class KeyAlreadyExistsException(e: String) extends Exception(e) 

    def underlying: scala.collection.immutable.Map[A, B] 

    def update [B1 >: B] (key: A, value: B1): Unoverwriteable[A, B1] = { 
     if (this contains(key)) throw new KeyAlreadyExistsException(
      "key already exists in WritableOnce map: %s".format(key.toString) 
     ) 
     underlying update (key, value) 
    } 

    def get(key: A): Option[B] = underlying get key 
    def elements: Iterator[(A, B)] = underlying.elements 
    def -(key: A): Unoverwriteable[A,B] = underlying - key 
    def empty[C]: Unoverwriteable[A,C] = underlying.empty[C] 
    def size: Int = underlying.size 
} 

Ensuite, vous définissez l'implicite dans l'objet compagnon:

object Unoverwriteable { 
    implicit def unoverwriteableMap[A, B](map0: scala.collection.immutable.Map[A, B]): Unoverwriteable[A, B] = 
     new Unoverwriteable[A, B] { def underlying = map0 } 

} 

Pour l'utiliser ajouter une annotation de type unwriteable à votre carte. Si vous décommentez les 2 dernières lignes de la méthode principale, vous obtenez une KeyAlreadyExistsException comme vous le souhaitez.

object UOMain { 
    def main(args: Array[String]): Unit = { 
     val map0 = Map((1 -> 1), (2 -> 2)): Unoverwriteable[Int, Int] 
     println("map0="+ map0) 

     val map1 = map0 - 2 
     println("map1="+ map1) 

     //val map2 = map1 + (1 -> 1000) 
     //println("map2" + map2) 
    } 
} 
Questions connexes