简体   繁体   中英

Scala: cast an object and assign it to a variable

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!

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.

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.

PS: 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM