Sahil Sareen
Sahil Sareen

Reputation: 1834

Scala dependency injection with abstract classes

Base class

 abstract class BaseSourceReader[T <: BaseSource] {

   /**                                                                                                                                                                               
    * Method to read data from source                                                                                                                                               
    */
   def readFromSource(identifier: T): Seq[String]
 }

// Trait common to all source identifiers
trait BaseSource

Derived class

 class XYZSourceReader(param1: String) extends BaseSourceReader[XYZBaseSource] {

   override def readFromSource(identifier: XYZBaseSource): Seq[String] = 
     // some implementation
 }

 case class XYZBaseSource(
     paramA: String,
     paramB: Seq[String]) extends BaseSource

Now what I want to do is inject the base source reader to a generic class to make the implementation independent of source:

 class MySourceTrasformerJob(
     val sourceReader: BaseSourceReader[BaseSource]) {
     // some code
 }

And use it like this:

 class MyTransformerJobApp {
    val reader = new XYZSourceReader(param)
    val job = MySourceTrasformerJob(reader)
 }

For this code snippet to work, the compiler suggests class SourceReader is invariant in type T. You may wish to define T as +T instead. (SLS 4.5)

I tried updating the BaseSourceReader:

- abstract class BaseSourceReader[T <: BaseSource] { + abstract class BaseSourceReader[+T <: BaseSource] {

but that leads to an error with the readFromSource method error: covariant type T occurs in contravariant position in type T of value identifier.

One working solution that I found is tying the implementation to the source but its not "generic" enough implementation:

 class MySourceTrasformerJob(
     val sourceReader: BaseSourceReader[XYZBaseSource]) {
     // some code
 }

I am kind of stuck in a loop with this and trying out model updates, any suggested way to handle this but definitely sticking to the generic abstract class dependency injection?

Upvotes: 0

Views: 352

Answers (1)

shayan
shayan

Reputation: 1241

You can get into that trouble if you have a type that needs to both be and not be covariant (or contravariant) at the same time.(which is impossible to implement. In such cases all you can do is leave it invariant) you can avoid this by re-writing your MySourceTransformerJob like this:

case class MySourceTransformerJob[T <: BaseSource](sourceReader: BaseSourceReader[T])

I did this and with no further modification your code compiled. This also is a more natural way of expressing your intent. After all you are designing your BaseSourceReader[T <: BaseSource]to be generic in type T with a lower bound. So MySourceTransformerJob had better ask for the same requirement on T if it is not by design putting any limitations of it's own on it.

Upvotes: 2

Related Questions