2010-02-13 3 views
5

Я хочу, чтобы карта, которая бросает попытку переписать значение для существующего ключа. Я пробовал:Расширение коллекции 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) 
} 

и получил:

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

Я совершенно новой для Scala и не может придумать способ, чтобы преодолеть это. Любая помощь? :)

редактировать: Я использую Scala 2.8.0.Beta1-пререлиз (который приносит некоторые изменения в scala.collection)

ответ

4

Как вы переопределение методов в Map, вы не можете определить черту, как тип возврата.

Самым простым решением является просто опускаем типов:

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

Или вы могли бы быть явным и добавить супер тип:

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] 

Я думаю, что вы бы только переопределить + хотя, поскольку ваши другие методы делегируют только Map.

4

Это фиксированное вашу ошибку компиляции:

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

Однако, я думаю, что вы действительно хотите, чтобы украсить collection.mutable.Map#+= следующим образом:

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

В случае, если это не понятно, почему вы хотите расширить 'collection.mutable.Map', это потому, что когда вы расширяете непреложную карту, каждый вызов' + 'дает вам _new map_. Поскольку вы создаете новую карту с призывом к супер, эта новая карта не будет невосприимчива! Есть два выхода: переопределить все, а не вызовы супер, но с вашими подпрограммами, которые берут старый неизменный unoverwriteable и создают новый с новым элементом (если разрешено); или, используйте измененную карту и продолжайте добавлять к той же карте вместо ее замены. Последнее намного меньше. –

3

Вы можете сделать это с помощью scala.collection.immutable .Map с небольшой неявной магией. То есть вы определяете один дополнительный метод в интерфейсе и неявное преобразование. Вот как я буду делать это в версии 2.7, я уверен, что в 2.8 есть несколько способов переопределить, но вы должны получить общую идею.

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 
} 

Затем вы определяете неявное в объекте компаньона:

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

} 

Чтобы использовать его, добавить UNWRITEABLE аннотацию типа к вашей карте. Если вы раскомментируете последние 2 строки в основном методе, вы получите исключение KeyAlreadyExistsException.

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) 
    } 
} 
Смежные вопросы