I want a Map that throws on attempt to overwrite a value for existing key. I tried:
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)
}
and got:
<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)
^
I'm quite new to Scala and can't figure out a way to overcome this. Any help? :)
edit: I'm using Scala 2.8.0.Beta1-prerelease (which brings some changes to scala.collection)
To extend a class in Scala we use extends keyword. there are two restrictions to extend a class in Scala : To override method in scala override keyword is required. Only the primary constructor can pass parameters to the base constructor.
Classes, case classes, objects, and (yes) traits can all extend no more than one class but can extend multiple traits at the same time. Unlike the other types, however, traits cannot be instantiated. Traits look about the same as any other type of class. However, like objects, they cannot take class parameters.
Case classes can't be extended via subclassing. Or rather, the sub-class of a case class cannot be a case class itself. Final prevents extending a case class with anything ( i.e. case and not case classes), in addition to the built-in restriction to extend case classes with case classes.
You can't extend multiple classes, but you can extend several traits. Unlike Java interfaces, traits can also include implementation (method definitions, data members, etc.). There is still a difference in that you can't instantiate a trait directly (similar to abstract classes in a way).
This fixed your compile error:
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)
}
However, I think you really want to decorate the collection.mutable.Map#+=
, as follows:
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)
}
}
As you are overriding methods in Map
, you can't define your trait as the return type.
The easiest solution is to just omit the types:
abstract override def + [B1 >: B] (kv: (A, B1)) = { /* ... */ }
// ...
abstract override def -(key: A) = super.-(key)
Or you could be explicit and add the super type:
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]
I think you would only have to override +
though, as your other methods only delegate to Map
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With