在 scala 中实现上述内容的惯用方法是什么?
通过指定适当的要求T
,或使用类型类来提供所需的行为。我稍后再谈这个。
C++模板程序员正确的理解方式是什么
scala 中泛型的限制? (为什么我不能在 scala 中执行此操作?例如
是因为泛型是在实例化之前编译的吗?)
C++ 模板在使用站点“处”进行编译,并且为模板参数的每个组合生成不同的代码。所以如果你使用上面的类int
and double
,你会得到两个不同的Point2
类已编译。
基本上,C++ 模板是宏,尽管远没有那么愚蠢#define
宏。事实上,C++ 模板是图灵完备的。也许将来有可能完成类似的事情,为 Scala 2.11 及更高版本计划即将推出的宏功能,但现在让我们忽略这一点。
类型参数(Scala 相当于 Javagenerics)不要改变代码的编译方式。参数化类在编译时生成其字节码,而不是在使用时生成。所以,当一个人实例化一个Point2
with Double
,生成字节码已经太晚了。
这意味着参数化类生成的代码必须与类可以实例化的所有类型兼容.
这就是麻烦的根源:任何调用的方法T
必须知道存在于T
当时Point2
已编译。所以,T
必须定义为具有定义此类方法的特征或类的上限,如您在示例中所示。
当然,正如您正确指出的那样,这并不总是可能的,这就是类型类的用武之地。类型类是为其定义了一组行为的一组类型。在 Scala 中实现的类型类被定义为其实例定义其他类的行为的类。
在您给出的示例中,您将使用Numeric
类型类,或者Fractional
如果您还需要分数除法,请键入 class 。类型类使用的一个简单示例是:
scala> import scala.math.Numeric
import scala.math.Numeric
scala> def sum[T](x: T, y: T)(implicit num: Numeric[T]): T = num.plus(x, y)
sum: [T](x: T, y: T)(implicit num: scala.math.Numeric[T])T
或者,使用称为“上下文边界”的特殊符号,
scala> def sum[T : Numeric](x: T, y: T): T = implicitly[Numeric[T]].plus(x, y)
sum: [T](x: T, y: T)(implicit evidence$1: scala.math.Numeric[T])T
符号T : Numeric
可以读作T
这样就有一个隐式实例Numeric[T]
可用的。代码implicitly[X]
返回类型的隐式值X
如果可以找到(或在编译时失败)。
现在,请注意没有方法被调用x
and y
-- 相反,我们调用方法num
谁的班级是Numeric[T]
。班上Numeric[T]
有一个方法plus
它知道如何添加两个T
s.
因为我们需要的是类型类实例,所以可以轻松添加新类型来满足类型类。人们可以很容易地声明一个Numeric
类型类Point2
(假设它的所有方法都可以实现):
class Point2Numeric[T](implicit num: Numeric[T]) extends Numeric[Point2[T]] {
def plus(x: Point2[T], y: Point2[T]): Point2[T] = x + y
// etc
}
implicit def ToPoint2Numeric[T : Numeric] = new Point2Numeric[T]
有了这个,那么对于任何T
其中有一个Numeric[T]
,还会有一个Numeric[Point2[T]]
.
除了普通类型继承(类型上限)之外,类型类是 Scala 中最常见的类型约束形式。还有其他更复杂的形式,对于它们是类型类还是不同的东西(例如磁铁模式)存在一些讨论。看着无形的 https://github.com/milessabin/shapeless举个例子来说明人们可以在这些事情上走多远。
另一种类型约束曾经很常见,但现在使用得更加谨慎,它们是视图边界。我不会详细介绍(事实上,搜索上下文边界和视图边界以从我自己那里找到有关它的长答案),但它们可以用于使类型类在使用时更具可读性。例如:
scala> import scala.math.Numeric.Implicits._
import scala.math.Numeric.Implicits._
scala> def sum[T : Numeric](x: T, y: T): T = x + y
sum: [T](x: T, y: T)(implicit evidence$1: scala.math.Numeric[T])T
导入的定义包含隐式转换,可以使用类型的值T
其中有一个Numeric[T]
就好像他们自己有这样的方法+
or -
.
最后一点,重要的是要认识到这会经历许多间接级别,因此可能不太适合高性能代码。