Reputation: 5702
I have a special Map that will eventually do some consistency checking of values, which are restricted to have special meaning. For now I just want to create a Schema that acts exactly like a Map[String, Any], in particular I'd like to instantiate is with a list of mappings, and not force the types for the Map to be specified, so they are always [String, Any]. So instead of
val myMap:Map[String,Any] = Map("one" -> 1, "two" -> "2", ...)
I'd like to be able to have:
val mySchema:Schema = Schema("one" -> 1, "two" -> "2", ...)
Map is a trait so I think I need to extend a class like HashMap
class Schema extends HashMap[String, Any]
when I instantiate it with a list of initial mappings I get
val mySchema = new Schema("one" -> 1, "two" -> "2", ...)
Error:(110, 19) too many arguments for constructor Schema: ()drivers.Schema
val mySchema = new Schema("one" -> 1, "two" -> "2")
^
There is some magic inside HashMap that is far beyond me to read (it extends
1 class with
5 traits). But it looks like the constructor's "contents" (a list of mappings?) are passed to something's initWithContents(contents)
pseudo constructor. Do I need something like that there?
Upvotes: 3
Views: 5444
Reputation: 2492
You'll notice that the syntax you're using to create the Schema
instance is slightly different than your target syntax as well as the standard Map
syntax:
Schema("one" -> 1, "two" -> "2", ...)
new Schema("one" -> 1, "two" -> "2", ...)
Map("one" -> 1, "two" -> "2", ...)
In particular, there is a new
in the second case. When you create a Map
without new, you are invoking a function called apply
on a companion module of the same name. In this case, the function which accepts a variable length sequence of arguments is defined the Map
object's inherited GenMapFactory
type and it has this signature:
def apply[A, B](elems: (A, B)*): CC[A, B] = (newBuilder[A, B] ++= elems).result
The magic derives the from Scala compiler resolving this method, because methods in the companion module are part of its implicit scope. You will need to do something similar in by adding a Schema
object:
object Schema {
def apply(entries: (String, Any)*) = new Schema() ++ entries
}
If you define the object
, this will work:
Schema("one" -> 1, "two" -> "2") // scala.collection.immutable.Map[String,Any](one -> 1, two -> 2)
One downside of this approach is that you lose type specificity after construction, but if you're just performing consistency checking on creation this may be enough. If you want the Schema
type to be returned on invocation of the apply
function or returned by other methods calls implemented in super types and enclosing scope, you will probably need to integrate with the Scala 2.8 collections API as documented here.
Upvotes: 2
Reputation: 6308
class Schema(elems: Tuple2[String, Any]*) extends HashMap[String, Any] {
this ++= elems
}
val mySchema = new Schema("one" -> 1, "two" -> "2")
Explanation:
->
is syntactic sugar for a tuple, so the constructor type is a variable number of tuplesUnfortunately, this does not work for an immutable HashMap. As far as I can tell, the only way to "extend" an immutable HashMap is by creating a class that holds an internal reference to one, as described e.g. in this answer to a similar question on SO.
Upvotes: 5