2010-02-13 106 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_。由於您正在通過調用super來創建新地圖,因此新地圖不會無法覆蓋!有兩種方法:重寫所有內容,而不是調用super,但用自己的例程取代舊的不可改寫的不可重寫的,並用新元素創建一個新的元素(如果允許);或者,使用可變映射並繼續添加到相同的映射而不是替換它。後者是少得多的工作。 – 2010-02-14 19:09:36

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 } 

} 

使用它的一個不可寫類型註釋添加到您的地圖。如果取消註釋main方法中的最後兩行,則會根據需要獲得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) 
    } 
}