Scala Type Erasure on case 类 如何解决

Scala Type Erasure on case classes how to solve

我有一个特征和两个扩展它的案例 classes:

trait Authenticatable {
  val email: String
  val pass: String
  val id: Long
  val sessionid: String
}

case class Admin(
  id: Long,
  email: String,
  pass: String,
  sessionid: Option[String] = None) extends Authenticatable

case class Client(
  id: Long,
  email: String,
  pass: String,
  sessionid: Option[String] = None) extends Authenticatable

我有一些功能 应该对用户进行身份验证,使用新的 sessionid 和 return 复制对象。

  def auth(email: String, password: String): Try[Admin] ={
    checkPass(models.Admin.findBy(sqls"email = $email"), password)
  }

  def auth(email: String, password: String, customer: Customer): Try[Customer] ={
    checkPass(models.Customer.findBy(sqls"email = $email"), password)
  }

  private def checkPass (model: Option[Authenticatable], password: String): Try[Authenticatable]={
    model match {
      case Some(m) => check(password, m.pass).map(_ => m)
      case _ => Failure(new Exception("Authentication failure!"))
    }
  }

问题是:我无法在 auth 函数中复制对象,因为函数 checkPass returns Authenticatable 而不是 Client 或 Admin class 并且 Authenticatable 没有 case 的复制方法classes.

解决这个问题的正确方法是什么?

我建议向 Authenticable 添加一个抽象方法,它设置会话 ID,并通过使用生成的 copy 方法由个别案例 类 实现。

trait Authenticable {
  def email: String
  def pass: String
  def id: Long
  def sessionid: Option[String]
  def setSessionId(id: String): Authenticable
}

case class Admin(
  id: Long,
  email: String,
  pass: String,
  sessionid: Option[String] = None) extends Authenticable {
  def setSessionId(id: String) = copy(sessionid = Some(id))
}

case class Client(
  id: Long,
  email: String,
  pass: String,
  sessionid: Option[String] = None) extends Authenticable {
  def setSessionId(id: String) = copy(sessionid = Some(id))
}

如果您使用类型参数,您可以避免丢弃 checkPass 将始终 return 相同类型的 Authenticable 的信息:

private def checkPass[A <: Authenticatable](model: Option[A], password: String): Try[A] =
  // exactly the same body here

这意味着在 auth 你可以有例如:

def auth(email: String, password: String): Try[Admin] =
  checkPass(models.Admin.findBy(sqls"email = $email"), password)
    .map(_.copy(sessionid = Some("whatever")))