I am going to create wrapper over JDBC ResultSet in Scala.
This wrapper is intended to be a function ResultSet => ParticularType
.
The problem is I can't find a common solution for making MultiMaps.
Here is the way I am fetching collections:
abstract class CollectionCreator[C] extends (ResultSet => C) { def apply(rs: ResultSet): C = { do { append(rs) } while (rs.next) returnCollection() } def append(rs: ResultSet) def returnCollection(): C }
Next goes maps creation. It is an implementation of collection creation and it is not abstract due to map non-abstractness (it is always backended with HashMap in my implementation).
In my opinion, it should look like this:
class MapCreator[K,IV](keyCreator: ResultSet => K, valueCreator: ResultSet => IV) extends CollectionCreator[Map[K, Place for V]] { type V = IV val intermediateMap = new HashMap[K, V] override def append(rs: ResultSet) { appendToMap(keyCreator(rs), valueCreator(rs)) } def appendToMap(key: K, value: IV) { intermediateMap(key) = value } override def returnCollection(): Map[K,V] = intermediateMap.toMap }
If it works, I would write ListMultiMap creation this way:
class ListMultiMapCreator[K,IV](keyCreator: ResultSet => K, valueCreator: ResultSet => IV) extends MapCreator[K, Place for V](keyCreator, valueCreator) { override type V = List[IV] override def appendToMap(key: K, value: IV) { intermediateMap(key) = intermediateMap.get(key) match { case Some(values) => values.::(value) case None => List(value) } } }
The problem is I can't use V
at Place for V
because it is not declared then.
I feel like abstract types is the good solution but don't know how to treat them right.
What is the correct way of such a collections creation?
I am also not sure if it is possible to override abstract types that were already defined higher in the class hierarchy.
In your definition, MapCreator
is really constraining IV
and V
to be the same type. By contract the V
returned in Map[K,V]
has to be the same type as the type returned by valueCreator
. For instance, if I called:
MapCreator((rs:ResultSet) => rs.getString("KEY"),
(rs:ResultSet) => rs.getString("VALUE"))(resultSet)
I would expect to get a Map[String,String]
. You cannot change that relation if you extend MapCreator
. If you want a Map[String,List[String]]
you need to supply a valueCreator
of type (ResultSet) => List[String]
.
With that in mind you can change the definition and implementation like this:
class MapCreator[K,IV](keyCreator: ResultSet => K,
valueCreator: ResultSet => IV) extends CollectionCreator[Map[K, IV]] {
val intermediateMap = new HashMap[K, IV]
def append(rs: ResultSet) { appendToMap(keyCreator(rs), valueCreator(rs)) }
def appendToMap(key: K, value: IV) { intermediateMap(key) = value }
def returnCollection(): Map[K, IV] = intermediateMap.toMap
}
class ListMultiMapCreator[K,IV](keyCreator: ResultSet => K,
elemCreator: ResultSet => IV) extends
MapCreator[K, List[IV]](keyCreator,
(rs:ResultSet) => List(elemCreator(rs))) {
override def appendToMap(key: K, value: List[IV]) {
intermediateMap(key) = intermediateMap.get(key) match {
case Some(values) => values.:::(value)
case None => value
}
}
}
I feel that because CollectionCreator
uses a type parameter, that it will be cumbersome to use abstract types. Overall, there seems to be much boiler plate. I would leverage more of the scala libraries:
def mapCreate[K, IV](rs: ResultSet,
keyCreator: ResultSet => K,
valueCreator: ResultSet => IV) = {
Iterator.continually(rs).takeWhile(_.next).map{rs =>
keyCreator(rs) -> valueCreator(rs)}.toMap
}
def listMultiMapCreate[K, IV](rs: ResultSet,
keyCreator: ResultSet => K,
valueCreator: ResultSet => IV) = {
Iterator.continually(rs).takeWhile(_.next).map{rs =>
keyCreator(rs) -> valueCreator(rs)}.toList.groupBy(_._1)
}
Also in your do { append(rs) } while (rs.next)
what if the result set is empty?