Home > Blockchain >  How to return subtype of a generic trait in scala?
How to return subtype of a generic trait in scala?

Time:02-27

I am trying to create a factory pattern, and the return type is a parametrized trait, and the actual return type will be a subtype of that trait, but I do not know the generic type beforehand, so I don't know how to specify the return type. A simplified version is like this:

trait Mapper[T] {
    def createMap(a: T, b: T): T
}

class MapperA extends Mapper[String]{
    override def createMap(a: String, b: String): String = {
        a   b
    }
}

class MapperB extends Mapper[Int]{
    override def createMap(a: Int, b: Int): Int = {
        a   b   b   b
    }
}

def factory(typeOfMapper: String): Mapper = {
    typeOfMapper match {
        case "mapperA" => new MapperA()
        case "mapperB" => new MapperB()
    }
}

val mapper = factory("mapperA")

This will give me and error of

trait Mapper takes type parameters

but I do not know the generic type beforehand. What should the return type be for the factory method here?

CodePudding user response:

Well, you could return Mapper[_] ... that will compile, but isn't really very useful, as pointed out in the comments: you won't be able to use the returned value in any meaningful way, because actual type of create will be unknown.

If different instances of your mapper will always have different type parameters (at least, within the same scope), then a neat solution would be using implicits:

object Mapper { 
   implicit def strMapper: Mapper[String] = new MapperA
   implicit def intMapper: Mapper[Int] = new MapperB
}

Then everywhere you have these visible, you can just do:

      val m1: Mapper[String] = implicitly[Mapper[String]] 
      val m2: Mapper[Int] = implicitly[Mapper[Int]]

You could also write your factory function (though, I am not sure why you'd want to) like this:

    def mapperFactory[T: Mapper] = implicitly[Mapper[T]]

and use it like this:

     val m: Mapper[String] = mapperFactory

or like this

    def intMapper = mapperFactory[Int]   

If you want different mappers for the same type parameter, it's basically the same idea, except it does't look as neat without implicits. The key is different factories for different types:

   class Mapper {
       def str(`type`: String): Mapper[String] = `type` match { 
         case "foo" => FooMapper()
         case "bar" => BarMapper()
       }
       def int(`type`: String): Mapper[Int] = `type` match {
          case "baz" => BazMapper()
          case "bak" => BakMapper()
       }
   }

   val fooMapper = Mapper.str("foo")
   val bakMapper = Mapper.int("bak")
  • Related