Reputation: 13983
I have the mappings
value:
val mappings: Map[Class[_] ,Iterable[AttributeKeyAndValue] => AnyRef]
is it possible to make it more typesafe like
val mappings: Map[Class[T], Iterable[AttributeKeyAndValue] => T]
where T
plays the same role as underscore. I'd expect compiler to complain, if it meets this code:
val mappings: Map[Class[T], Iterable[AttributeKeyAndValue] => T] = Map(
classOf[String], attrs => 1)
Upvotes: 1
Views: 1161
Reputation: 40500
Wrap it around into a case class?
type Attrs = Iterable[AttributeKeyAndValue]
case class Mappings[T](m: Map[Class[T], Attrs => T]
object Mappings {
implicit def mk[T](m: Map[Class[T], Attrs => T): Mappings[T] = Mappings(m)
}
val mappings: Mappings[_] = Map(classOf[String] -> { (_:Attrs) => "foo" }) // Works
val badMappings: Mappings[_] = Map(classOf[String] -> { (_:Attrs) => 1 }) // Fails
Upvotes: 0
Reputation: 170733
Wildcards in Scala are just a specific simple case of existential types, and what you want would be a more complex one because you want to use the same T
in two places. Something like Seq[(Class[T], AttributeKeyAndValue => T) forSome { type T }]
. But note where you need to put forSome
: there is no equivalent place if you want Map
! E.g. Map[Class[T], AttributeKeyAndValue => T] forSome { type T }
would mean there is a single T
for the entire map.
What I'd suggest is creating a type which presents a more type-safe interface, even if you need casts inside:
class Mappings private (contents: Map[Class[_], Iterable[AttributeKeyAndValue] => AnyRef]) {
def get[T](clazz: Class[T]) = contents.get(clazz).asInstanceOf[Option[Iterable[AttributeKeyAndValue] => T]]
def +[T](clazz: Class[T], value: Iterable[AttributeKeyAndValue] => T) = new Mappings(contents + (clazz, value))
// any other methods you want
}
object Mappings {
val empty = new Mappings(Map.empty)
}
// elsewhere
Mappings.empty + (classOf[String], attrs => "a") // type-checks
Mappings.empty + (classOf[String], attrs => 1) // doesn't type-check
You can actually improve the API to avoid manually passing classes, so you just write get[String]
and +(attrs => 1)
automatically infers it needs classOf[Int]
, but I decided to show the simple idea here.
Upvotes: 2
Reputation: 6385
Well, compiler does not know what is T
in example you've provided.
So, as an option you can defined mappings
function parametrised by T
:
def mappings[T](x: AttributeKeyAndValue => T):
Map[Class[T], AttributeKeyAndValue => T] = Map(classOf[T] -> x)
usage:
val mappping = mappings(x => x.toString)
and the type that compiler will be able to infer is:
mappping : Map[Class[String], AttributeKeyAndValue => String]
Upvotes: 0
Reputation: 2453
You can't parametrize val
s so no, not like that.
Looking at your request, it doesn't make much sense. Lets say that this: val mappings: Map[Class[T], Iterable[AttributeKeyAndValue] => T]
was valid and the compiler would complain.
You would either parametrize all the entries in the map with the same type, ie. T
or have each entry with it's own parametrized type making it impossible to know which type it is when retrieving the entries with the apply
or get
methods.
I suggest you stick with the Class[_]
because the only way to parametrize this is to force all the entries to have the same type. For example if you were able to parametrize it in Map[Class[String], ...]
then you would only be able to put 1 entry in the map, the one where the key is classOf[String]
Upvotes: 2