2010-02-13 35 views
5

Quiero un mapa que arroja al intentar sobreescribir un valor para la clave existente. Probé:Extendiendo una colección de 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) 
} 

y tengo:

<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) 
                     ^

Soy bastante nuevo en Scala y no puedo encontrar una manera de superar esto. ¿Alguna ayuda? :)

editar: Estoy usando Scala-2.8.0.Beta1 preliminar (que trae algunos cambios en scala.collection)

Respuesta

4

Como está sustituyendo los métodos de Map, no se puede definir como el rasgo el tipo de devolución.

La solución más fácil es simplemente omitir los tipos:

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

O usted podría ser explícita y agregar el tipo súper:

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] 

creo que sólo tendría que anular + sin embargo, como sus otros métodos solo delegan en Map.

4

que esto esté arreglado el error de compilación:

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) 
} 

Sin embargo, creo que realmente quiere decorar la collection.mutable.Map#+=, de la siguiente manera:

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

En caso de que no está claro por qué desea ampliar 'collection.mutable.Map', es porque cuando extiende un mapa inmutable, cada llamada a' '+ le da una mapa_ _new. ¡Ya que está creando el nuevo mapa con una llamada a super, ese nuevo mapa no será irrevocable! Hay dos formas de salir: anular todo, no con llamadas al súper, sino con sus propias rutinas que toman un viejo inmutable no verificable y crear uno nuevo con el nuevo elemento (si está permitido); o bien, use un mapa mutable y siga agregando al mismo mapa en lugar de reemplazarlo. Este último es mucho menos trabajo. –

3

usted puede hacerlo utilizando un scala.collection.immutable .Mapa con un poco de magia implícita. Es decir, define un método adicional en la interfaz y una conversión implícita. Así es como lo haría en 2.7, estoy seguro de que hay diferentes métodos para anular en 2.8, pero debería tener una idea general.

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 
} 

A continuación se definen los implícito en el objeto acompañante:

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

} 

para usarlo añadir una anotación de tipo unwriteable a su mapa. Si eliminas las dos últimas líneas del método principal, obtienes una excepción KeyAlreadyExistsException si lo deseas.

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) 
    } 
} 
Cuestiones relacionadas