Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending a Scala collection

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)

like image 498
Artur Gajowy Avatar asked Feb 13 '10 22:02

Artur Gajowy


People also ask

How do I extend a class in Scala?

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.

Can an object be extended in Scala?

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.

Can you extend a case class in Scala?

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.

Can you extend multiple classes in Scala?

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


2 Answers

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)
  }
}
like image 162
retronym Avatar answered Oct 10 '22 22:10

retronym


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.

like image 34
Fabian Steeg Avatar answered Oct 10 '22 23:10

Fabian Steeg