Reputation: 25366
I want to cast myObject to a specific class and assign it to a variable myTargetObject. I have the following Scala code, but there is error at the cast:
def findResult[T](myObject: T): String = {
var myTargetObject= None
if (myObject.isInstanceOf[myClass1]) {
myTargetObject = myObject.asInstanceOf[myClass1]
} else if (myObject.isInstanceOf[myClass2]) {
myTargetObject = myObject.asInstanceOf[myClass2]
} else (myObject.isInstanceOf[myClass3]) {
myTargetObject = myObject.asInstanceOf[myClass3]
}
}
What should be the right syntax for this purpose? Thank you very much!
Upvotes: 1
Views: 456
Reputation: 1905
IMHO the most idiomatic way to do it in Scala is using type classes:
class MyClass1(val s: String)
class MyClass2(val s: String)
trait ResultFinder[T] {
def findResult(t: T): String
}
object ResultFinder {
def findResult[T: ResultFinder](t: T): String = implicitly[ResultFinder[T]].findResult(t)
implicit object RFMyClass1 extends ResultFinder[MyClass1] {
override def findResult(t: MyClass1): String = t.s + " of MyClass1"
}
implicit object RFMyClass2 extends ResultFinder[MyClass2] {
override def findResult(t: MyClass2): String = t.s + " of MyClass2"
}
}
REPL session:
scala> import ResultFinder.findResult
import ResultFinder.findResult
scala> val c1 = new MyClass1("hello")
c1: MyClass1 = MyClass1@39fb3ab6
scala> val c2 = new MyClass2("hello")
c2: MyClass2 = MyClass2@3fee9989
scala> findResult(c1)
res0: String = hello of MyClass1
scala> findResult(c2)
res1: String = hello of MyClass2
This solution is completely type safe. There's no cast, not even under the hood. Pattern matching technique, instead, uses cast under the hood. Another bonus of this solution is that it works for more complex types like List[MyClass1]
provided there's an implicit instance of it in the scope. Pattern matching wouldn't work for List[MyClass1]
because of type erasure.
P.S.: I suppose you used myTargetObject
as a temp variable but you don't actually need it because the only way to get out of findResult
meaningfully is by returning a String
.
Upvotes: 2
Reputation: 10613
The problem is when you do var myTargetObject= None
, it infers the type to be Option[Any]
None
, so trying to reassign it as myClassN
is going to fail.
The correct way to do this is with matching:
def findResult[T](myObject: T): String = {
myObject match{
case myTargetObject:MyClass1 => {
//Actions for MyClass1
}
case myTargetObject:MyClass2 => {
//Actions for MyClass2
}
case myTargetObject:MyClass3 => {
//Actions for MyClass3
}
case other => {
//Default action
}
}
}
This will let you use it as the desired type, while still being completely type-safe. Even this is still less than ideal though, since you don't generally want behavior to be specialized for each class in the calling code. You should try to refactor this into using common interfaces, so that it can all be handled in the same way.
Upvotes: 4