用类型 class 扩展案例 class

Extend case class with typeclass

我有这个代码

  case class Salary(employee: String, amount: Double){

  }

  trait XN[M] {
    def x2(m: M): M
    def x3(m: M): M
    def x4(m: M): M
  }

    

我想用 XN 特征扩展 Salary,以便进行以下测试:

test("Salary is extended with xN") {

    val bobSalary = Salary("Bob", 100.0)

    bobSalary.x2 shouldBe Salary("Bob", 200.0)
    bobSalary.x3 shouldBe Salary("Bob", 300.0)
    bobSalary.x4 shouldBe Salary("Bob", 400.0)

  }

我的尝试:

#1

  implicit val SalaryXN: XN[Salary] = new XN[Salary] {
    override def x2(m: Salary): Salary = m.copy(amount = m.amount * 2)

    override def x3(m: Salary): Salary = m.copy(amount = m.amount * 3)

    override def x4(m: Salary): Salary = m.copy(amount = m.amount * 4)
  }

#2

  object Salary extends XN[Salary] {
    override def x2(m: Salary): Salary = new Salary(employee = m.employee, amount = m.amount * 2)

    override def x3(m: Salary): Salary = new Salary(employee = m.employee, amount = m.amount * 3)

    override def x4(m: Salary): Salary = new Salary(employee = m.employee, amount = m.amount * 4)
  }

怎么做?

Online code

创建从 Salary 到 XN 的隐式转换可能是一个解决方案。这就是 scala 如何向现有 类 添加新功能而不扩展它们。

implicit def salary2XN(s: Salary): XN[Salary] = {
  new XN[Salary] {
    override def x2: Salary = Salary(s.employee, 2*s.amount)
    override def x3: Salary = Salary(s.employee, 3*s.amount)
    override def x4: Salary = Salary(s.employee, 4*s.amount)
  }
}

[使用公认的解决方案,隐式是棘手的!]

greg 开了个好头,但正如所写,它不适用于您的解决方案。

这里有一些变化:

仍在使用:

case class Salary(employee: String, amount: Double) // As before

您可以在 Salary 上调用该方法或将其传递给 Salary,您正在尝试同时执行这两项操作。

最简单的方法是尝试:

trait XN[M] {
    def x2: M
    def x3: M
    def x4: M
  }

implicit def salary2XN(s: Salary): XN[Salary] = {
    new XN[Salary] {
      override def x2: Salary = s.copy(amount = 2 * s.amount)
      override def x3: Salary = s.copy(amount = 3 * s.amount)
      override def x4: Salary = s.copy(amount = 4 * s.amount)
    }
  }

现在您可以使用您的签名调用每个方法:

Salary("bob", 200).x2

编辑:

使用普通旧扩展的解决方案:

trait XN[M] {
    def x2: M
    def x3: M
    def x4: M
  }

  case class Salary(employee: String, amount: Double) extends XN[Salary] {
    override def x2: Salary = Salary(employee, 2 * amount)
    override def x3: Salary = Salary(employee, 3 * amount)
    override def x4: Salary = Salary(employee, 4 * amount)
  }

您现在可以用完全相同的方式调用它:

Salary("bob", 233).x2

注意:在以前的实现中,Salarys 本身没有 XN 的方法,而是我们使用隐式转换将它们应用于每个 Salary。对于后一种实现,每个 Salary 都有 XN 的方法,我们在 case class 本身的实现中定义它。

因为 XN 似乎是一个 类型类 ,所以最好正确使用该模式而不是依赖 (气馁) 隐式转换.

trait XN[M] {
  def x2(m: M): M
  def x3(m: M): M
  def x4(m: M): M
}

object XN {
  object syntax {
    implicit class XNOp[M](private val m: M) extends AnyVal {
      @inline final def x2(implicit ev: XN[M]): M = ev.x2(m)
      @inline final def x3(implicit ev: XN[M]): M = ev.x3(m)
      @inline final def x4(implicit ev: XN[M]): M = ev.x4(m)
    }
  }
}

final case class Salary(employee: String, amount: Double)
object Salary {
  implicit final val SalaryXN: XN[Salary] =
    new XN[Salary] {
      override def x2(s: Salary): Salary = s.copy(amount = s.amount * 2)
      override def x3(s: Salary): Salary = s.copy(amount = s.amount * 3)
      override def x4(s: Salary): Salary = s.copy(amount = s.amount * 4)
    }
}

可以这样使用:

import XN.syntax._

val bobSalary = Salary("Bob", 100.0)

bobSalary.x2
// res: Salary = Salary("Bob", 200.0)

可以看到代码运行 here.