在scala中实现上述内容的惯用方式是什么?
通过指定适当的要求T
,或使用类型类来提供所需的行为。我稍后再谈。
C++模板程序员理解scala中泛型限制的正确方法是什么?(为什么我不能在 scala 中这样做?例如是因为泛型是在实例化之前编译的吗?)
C++ 模板在“使用”站点编译,并且为模板的每个参数组合生成不同的代码。因此,如果将上面的类与int
and一起使用,则会编译出double
两个不同的类。Point2
基本上,C++ 模板是宏,尽管远没有#define
宏那么愚蠢。事实上,C++ 模板是图灵完备的。也许将来有可能完成类似的事情,即将为 Scala 2.11 及更高版本计划的宏功能,但现在让我们忽略它。
类型参数(Scala 等价于 Java泛型)不会改变代码的编译方式。参数化类在编译时生成其字节码,而不是在使用时。因此,当用 实例化 aPoint2
时Double
,生成字节码为时已晚。
这意味着参数化类生成的代码必须与类可以实例化的所有类型兼容。
这就是问题的根源:任何调用的方法都T
必须知道T
在Point2
编译时存在。因此,T
必须定义为具有定义此类方法的特征或类的上限,如您在示例中所示。
当然,正如您正确指出的那样,这并不总是可能的,这就是类型类的用武之地。类型类是一组类型,为其定义了一组行为。在 Scala 中实现的类型类被定义为类,其实例定义了其他类的行为。
在您给出的示例中,如果您还需要分数除法,您将使用Numeric
类型类或类型类。Fractional
类型类使用的一个简单示例是:
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
而是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
有 a 的Numeric[T]
人,也会有 a Numeric[Point2[T]]
。
在普通类型继承(类型上限)之后,类型类是 Scala 中最常见的类型约束形式。还有其他一些更复杂的形式,对于它们是类型类还是不同的东西——例如磁铁模式,有一些讨论。以无形为例,了解人们可以采取多远的方式。
另一种过去很常见但现在更谨慎地使用的类型约束是视图边界。我不会详细介绍(实际上,搜索上下文边界和视图边界以从我自己那里找到关于它的长答案),但它们可以用于使类型类在使用时更具可读性。例如:
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
a 的类型值,就Numeric[T]
好像它们本身具有类似+
or的方法一样-
。
作为最后一点,重要的是要意识到这要经过许多间接级别,因此可能不太适合高性能代码。