Duncan McGregor
Duncan McGregor

Reputation: 18177

How to use companion factory objects as strategy?

I have a test which fails intermittently because of ordering issues when I iterate over the values in a Map.

Scala helpfully provides a ListMap which makes the tests stable, at the expense of performance. So I abstracted the ImmutableMapFactory as a val and use it in my code.

class C {
  val immutableMapFactory = scala.collection.immutable.Map

  def func = {
    ...
    immutableMapFactory(pairs :_*)
  }
}

Now my plan was to extend C and override immutableMapFactory for tests

class TestableC extends C {
  override val immutableMapFactory = scala.collection.immutable.ListMap
}

Unsurprising this fails as ListMap does not have the same type as Map. How should I specify the type of the val (or a def) so that I can use the factory wherever I need to create a Map?

Upvotes: 1

Views: 841

Answers (2)

Kevin Wright
Kevin Wright

Reputation: 49705

Your problem is in this line:

val immutableMapFactory = scala.collection.immutable.Map

This makes immutableMapFactory equal to the singleton object Map. ListMap (the singleton) is not a subclass of Map (the singleton), so the subsequent override fails.

If you instead take the apply method from Map, and partially apply it to form a first class function (of type (A, B)* => immutable.Map[A,B]) then the technique can be made to work:

import collection.immutable

class Bip {
  def fac[A,B] = immutable.Map.apply[A,B] _
}

class Bop extends Bip {
  override def fac[A,B] = immutable.ListMap.apply[A,B] _
}

Upvotes: 2

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297305

Two possible ways. First, using def and functions, which I think is a better abstraction.

class C {
  def immutableMapFactory[A,B]: ((A,B)*) => Map[A,B] = scala.collection.immutable.Map.apply _
}

class TestableC extends C {
  override def immutableMapFactory[A,B] = scala.collection.immutable.ListMap.apply _
}

Second, using val, and structural types:

class C {
  val immutableMapFactory: { def apply[A,B](t: (A,B)*): Map[A,B] } = scala.collection.immutable.Map
}

class TestableC extends C {
  override val immutableMapFactory: { def apply[A,B](t: (A,B)*): Map[A,B] } = scala.collection.immutable.ListMap
}

Upvotes: 3

Related Questions