I tried writing a function with a generic return type but it doesn't work unless I cast the return type. Please see the function getSomething()
below I expected it to work without the casting. What might I be doing wrong here?
trait Sup
class Sub extends Sup {
def getString = "I am Sub"
}
class Sub2 extends Sup {
def getInt = 100
}
def getSomething[A <: Sup](str: String) : A = {
str match {
case "sub" => getSub.asInstanceOf[A]
case "sub2" => getSub2.asInstanceOf[A]
}
}
def getSub(): Sub = {
new Sub
}
def getSub2() : Sub2 = {
new Sub2
}
val x = getSomething[Sub]("sub").getString
val y = getSomething[Sub2]("sub2").getInt
As Alexey mentions, the
instanceOf
is needed to force a link between the expected type and the type of the object returned. It's the equivalent of saying: "Compiler, trust me, I'm giving you an 'A'" and it's not very safe as it depends of us to provide a correct type.If we want the type system to figure things out for us, we need to give it some additional information. One way to do that in Scala is to define some factory that knows how to produce instances of our types and evidence that allows that factory to return our specific types.
This is a version of the code above introducing such construct and using
ContextBounds
to obtain the right factory instance of the type we want.The error messages you get without
asInstanceOf
tell you exactly what you are doing wrong. Incase "sub"
, the body gives aSub
, and the compiler has no reason to thinkA
is a supertype ofSub
(or thatSub
can be implicitly converted toA
).Suppose it worked. Then the following calls would be legal:
or
What should happen in either case?