基于泛型类型的函数,根据类型对数字求和

Dan*_*iel 6 scala

假设x和y是相同类型的,并且可以是Boolean,Int,或Double.这是我想写的函数:

f(x, y) = 
   - if x == Boolean ==>   !x 
   - if x == Integer or x == Double ==> x+ y 
Run Code Online (Sandbox Code Playgroud)

这样做的一种方法可以是以下方法.我想知道是否有人对此有更好的想法.

def fun[T](x: T, y: T): T { 
   x match { 
       case xP: Boolean => !xP 
       case xP: Double => y match { case yP: Double =>  xP + yP }
       case xP: Int => y match { case yP: Int =>  xP + yP }
   }
}
Run Code Online (Sandbox Code Playgroud)

我不乐意这样做的原因是,xy具有相同的类型.我不应该需要两个match-case; 对?

另外两件事:

  • 仅仅[T <: Int, Double, Boolean] 为了将类型限制为三种类型而设置是否足够?
  • 输出类型需要再次出现T.

Tra*_*own 11

这正是类型类旨在解决的问题.在你的情况下你可以写这样的东西:

trait Add[A] {
  def apply(a: A, b: A): A
}

object Add {
  implicit val booleanAdd: Add[Boolean] = new Add[Boolean] {
    def apply(a: Boolean, b: Boolean): Boolean = !a
  }

  implicit def numericAdd[A: Numeric]: Add[A] = new Add[A] {
    def apply(a: A, b: A): A = implicitly[Numeric[A]].plus(a, b)
  }
}
Run Code Online (Sandbox Code Playgroud)

类型值Add[X]描述了如何添加两个类型的值X.您Add[X]为每个X希望能够执行此操作的类型在范围中放置类型的隐式"实例" .在这种情况下,我提供了实例Boolean和具有实例的任何类型scala.math.Numeric(由标准库提供的类型类).如果你只是想为实例IntDouble,你可以简单地离开了numericAdd,写自己Add[Int]Add[Double]实例.

你写fun这样的:

def fun[T: Add](x: T, y: T) = implicitly[Add[T]].apply(x, y)
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

scala> fun(true, false)
res0: Boolean = false

scala> fun(1, 2)
res1: Int = 3

scala> fun(0.01, 1.01)
res2: Double = 1.02
Run Code Online (Sandbox Code Playgroud)

这具有非常显着的优点,即在运行时不会在尚未定义操作的类型上爆炸.MatchError当您传递两个字符串时fun,不会因为异常而使程序崩溃,而是会出现一个很好的编译失败:

scala> fun("a", "b")
<console>:14: error: could not find implicit value for evidence parameter of type Add[String]
       fun("a", "b")
          ^
Run Code Online (Sandbox Code Playgroud)

一般来说,"类型案例"匹配(即看起来像是匹配case x: X => ...)在Scala中是一个坏主意,而且几乎总有一个更好的解决方案.通常它会涉及类型类.

  • 另一点:没有(合理简单)的方法来完成类型约束,如`[T <:Int,Double,Boolean]`.您可以执行类似的操作,但它涉及使用实质上更复杂的类型类,而上述更简单,更不通用的版本通常更可取. (2认同)