Returning subclass of parameterized type in Scala

2019-09-16 02:59发布

问题:

I'm trying to return a subclass of a parameterized type Output[T <: Input] but for some reason I cannot seem to get the syntax right:

sealed trait Input
case class A(id: Int) extends Input
case class B(id: String) extends Input

sealed trait Output[+T <: Input]
case class OutA(some: String) extends Output[A]
case class OutB(thing: Int) extends Output[B]

def doStuff[T <: Input, RT <: Output[T]](input: T): RT = 
  input match {
    case A(i) => OutA(i.toString)
    case B(s) => OutB(s.toInt)
  }

// error: type mismatch;
//  found   : OutA
//  required: RT
//            case A(i) => OutA(i.toString)
//
// error: type mismatch;
//  found   : OutB
//  required: RT
//            case B(s) => OutB(s.toInt)

def doStuff[T <: Input](input: T): Output[T] = 
  input match {
    case A(i) => OutA(i.toString)
    case B(s) => OutB(s.toInt)
  }

// error: type mismatch;
//  found   : OutA
//  required: Output[T]
//            case A(i) => OutA(i.toString)

// error: type mismatch;
//  found   : OutB
//  required: Output[T]
//            case B(s) => OutB(s.toInt)

def doStuff[T <: Input, RT <: Output[_]](input: T): RT = 
  input match {
    case A(i) => OutA(i.toString)
    case B(s) => OutB(s.toInt)
  }

// error: type mismatch;
//  found   : OutA
//  required: RT
//            case A(i) => OutA(i.toString)

// error: type mismatch;
//  found   : OutB
//  required: RT
//            case B(s) => OutB(s.toInt)

In my actual code the Input and Output subclasses are wrapped in containers that I cannot modify and the input is also coming from another system over which I have no control. However, this seems to be the smallest example I could come up with for which I get the same compile-time type errors.

How can I solve my problem?

回答1:

You can do it this way (this is an example of a type class in Scala, searching for this term will give you quite a few posts explaining the pattern):

case class StuffDoer[T](doStuff: T => Output[T])
object StuffDoer {
  implicit val ADoer: StuffDoer[A] = StuffDoer(x => OutA(x.id.toString))
  implicit val BDoer: StuffDoer[B] = StuffDoer(x => OutB(x.id.toInt))
  implicit val InputDoer: StuffDoer[Input] = StuffDoer {
    case a: A => ADoer.doStuff(a)
    case b: B => BDoer.doStuff(b)
  }
}

def doStuff[T](input: T)(implicit doer: StuffDoer[T]) = doer.doStuff(input)

(Really, some variation of this problem is asked from time to time, but writing the answer is faster than searching for previous questions.)



标签: scala