简体   繁体   中英

Run function with generic parameter without using asInstanceOf

here is my sample scala code:

object App {
  abstract class BaseAction
  type ApiAction[T <: BaseAction] = (T) => Unit

  case class FirstAction(name: String) extends BaseAction
  case class SecondAction(surname: String) extends BaseAction

  def action1[Z <: BaseAction] = {  
    (a: Z) => { // Here i'would like to have a: FirstAction
      val z = a.asInstanceOf[FirstAction]
      println("Running action: " + z.name )
    }
   }

 def action2[Z <: BaseAction] = {
  (a: Z) => { // Here i'would like to have a: SecondAction
  val z = a.asInstanceOf[SecondAction]
  println("Running action " + z.surname )
 }
}

  def myActions[T <: BaseAction] = Map[String, ApiAction[T]]("a1" -> action1[T], "a2" -> action2[T])

  myActions("a1")(FirstAction("Action 1"))
  myActions("a2")(SecondAction("Action 2"))
}

I have few action functions, which does different things. Each action function receive one parameter: action class, where all action classes inherits from BaseAction abstract class.

Function myActions is Map of actionName to action function.

My code is working, but i think that using asInstanceOf is not good practice, and i want to know how can i write this code only using generic types, without asInstanceOf.

The thing is that you are doing a lot of "you should not do this stuff".

I will try to give you are "better" ( as per most scala people) way for writing the same thing. The changes include the use of information from type-bounds and that custom type ApiAction to write a more predictable and organised code.

First you have following abstractions,

  abstract class BaseAction

  type ApiAction[T <: BaseAction] = (T) => Unit

  case class FirstAction(name: String) extends BaseAction
  case class SecondAction(surname: String) extends BaseAction

Now you can use these abstractions to write your Actions object,

object MyActions {

  val action1: ApiAction[FirstAction] = {
    case FirstAction(name) => println("Running action :: " + name)
  }

  val action1Other: ApiAction[FirstAction] = (fa: FirstAction) => {
    println("Running action :: " + fa.name)
  }

  val action2: ApiAction[SecondAction] = {
    case SecondAction(surname) => println("Running action :: " + surname)
  }

  val action2Other: ApiAction[SecondAction] = (sa: SecondAction) => {
    println("Running action :: " + sa.surname)
  }

  // but lets say you wanted a generic ApiAction
  val actionGeneric: ApiAction[BaseAction] = {
    case FirstAction(name) => println("Running action :: " + name)
    case SecondAction(surname) => println("Running action :: " + surname)
  }

}

Now you can use these "actions" as you want in your app,

object MyApp extends App {

  MyActions.action1(FirstAction("Action 1"))

  MyActions.action1Other(FirstAction("Action 1 Other"))

  MyActions.actionGeneric(FirstAction("Action 1 Generic"))

  MyActions.action2(SecondAction("Action 2"))

  MyActions.action2Other(SecondAction("Action 2 Other"))

  MyActions.actionGeneric(SecondAction("Action 2 Generic"))
}

You can use idiomatic Scala pattern matching instead of asInstanceOf :

val z = a match {
  case FirstAction(name) => println("Running action " + name)
  case _ => println("Error")
}

Note that pattern matching still uses isInstanceof + asInstanceOf under the hood, but it's considered good practice, unlike calling asInstanceOf directly.

By the way, it would probably be wise to organize your code a bit differently so that you match only once, instead of having two separate "first action or error" and "second action or error" blocks:

def action[Z <: BaseAction] = {
  (a: Z) => a match {
    case FirstAction(name) => println("Running action " + name)
    case SecondAction(surname) => println("Running action " + surname)
    case _ => println("Error")
  }
}

def myActions[T <: BaseAction] = Map[String, ApiAction[T]]("a1" -> action[T], "a2" -> action[T])

myActions("a1")(FirstAction("Action 1"))
myActions("a2")(SecondAction("Action 2"))

// output:
// Running action Action 1
// Running action Action 2

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