Home > Software design >  Return Generic Polymorphic Return Type in Scala
Return Generic Polymorphic Return Type in Scala

Time:01-26

In an example below, I like to return any type of Container from the function getInfos and from the caller side to perform map with pattern matching however I get compiler error that I cannot return Container[_]. Is there a way to return Container of any type? If not, what is the best way to approach this?

  trait Info[T] {
    def value: T
  }

  case class ContainerAInfo(value: Long) extends Info[Long]
  case class ContainerBInfo(value: String, value2: String) extends Info[String]
  
  trait Container[T] {
    def info: Info[T]
  }
  
  case class ContainerA[Long](projectionInfo: ContainerAInfo)
  case class ContainerB[String](projectionInfo: ContainerBInfo)

  def getInfos: Seq[Container[_]] = {
    Seq(
      ContainerA(
        projectionInfo = ContainerAInfo(1L)
      ),
      ContainerB(
        projectionInfo = ContainerBInfo("12", "12")
      )
    )

  }

CodePudding user response:

Long and String in

case class ContainerA[Long](...)
case class ContainerB[String](...)

are not standard Long (scala.Long) and String (scala.Predef.String aka java.lang.String). You introduce new generics shadowing standard Long and String. It's better to switch on scalacOptions = "-Xlint:type-parameter-shadow" to avoid such confusion

Type Arguments and Bounds in Scala

What causes this type error in a pattern guard for matching list of tuples

Strange Error with String in Scala 2.12.7

Type parameters applied to Scala Function

I suspect ContainerA and ContainerB are supposed to extend Container, don't they? So a minimal change to your code making it compile is

case class ContainerA(info: ContainerAInfo) extends Container[Long]
case class ContainerB(info: ContainerBInfo) extends Container[String]

def getInfos: Seq[Container[_]] = {
  Seq(
    ContainerA(
      info = ContainerAInfo(1L)
    ),
    ContainerB(
      info = ContainerBInfo("12", "12")
    )
  )

}

Container[_] is an existential type, a minimal supertype of all Container[T], including Container[Long], Container[String], Container[Any]

scala - Any vs underscore in generics

Understanding scala's _ vs Any/Nothing

What is an existential type?

CodePudding user response:

You can consider making the hierarchy covariant and using Seq[Container[Any]]:

trait Info [ T] {
  def value: T
}

case class ContainerAInfo (value: Long) extends Info [Long]
case class ContainerBInfo (value: String, value2: String) extends Info [String]

trait Container [ T] {
  def projectionInfo: Info [T]
}

case class ContainerA (projectionInfo: ContainerAInfo) extends Container [Long]
case class ContainerB (projectionInfo: ContainerBInfo) extends Container [String]

def getInfos: Seq [Container [Any]] =
  Seq (
    ContainerA (
      projectionInfo = ContainerAInfo (1L)
    ), ContainerB (
      projectionInfo = ContainerBInfo ("12", "12")
    )
  )

Though possibly it will not be that useful.

Demo

  • Related