我希望有一个通用的矢量抽象类/特征来指定某些方法,例如:
trait Vec
{
def +(v:Vec):Vec
def *(d:Double):Vec
def dot(v:Vec):Double
def norm:Double
}
我希望Vec2D
和Vec3D
延长Vec
:
class Vec2D extends Vec { /* implementation */ }
class Vec3D extends Vec { /* implementation */ }
但是,我怎样才能使Vec2D
只能添加到其他Vec2D
而不是Vec3D
?
现在我只是在没有共同Vec2D
祖先的情况下实施Vec3D
和Vec
,但是重复代码会变得乏味。我必须实现依赖于这些类的所有几何类(例如Triangle
,Polygon
,Mesh
,...)两次,一次用于Vec2D
,另一次用于{ {1}}。
我看到java实现:Vec3D
和javax.vecmath.Vector2d
没有共同的祖先。这是什么原因?有没有办法在scala中克服它?
答案 0 :(得分:7)
您可以使用自我类型:
trait Vec[T] { self:T =>
def +(v:T):T
def *(d:Double):T
def dot(v:T):Double
def norm:Double
}
class Vec2D extends Vec[Vec2D] { /* implementation */ }
class Vec3D extends Vec[Vec3D] { /* implementation */ }
但如果两个实现非常相似,您也可以尝试在Dimension上进行抽象。
sealed trait Dimension
case object Dim2D extends Dimension
case object Dim3D extends Dimension
sealed abstract class Vec[D <: Dimension](val data: Array[Double]) {
def +(v:Vec[D]):Vec[D] = ...
def *(d:Double):Vec[D] = ...
def dot(v:Vec[D]):Double = ...
def norm:Double = math.sqrt(data.map(x => x*x).sum)
}
class Vec2D(x:Double, y:Double) extends Vec[Dim2D.type](Array(x,y))
class Vec3D(x:Double, y:Double, z:Double) extends Vec[Dim3D.type](Array(x,y,z))
当然,这取决于您希望如何表示数据,以及您是否希望拥有可变或不可变的实例。对于“真实世界”的应用程序,您应该考虑http://code.google.com/p/simplex3d/
答案 1 :(得分:5)
作为requested,设计基本特征的最有用方法包括CRTP 和 self-type annotation。
trait Vec[T <: Vec[T]] { this: T =>
def -(v: T): T
def *(d: Double): T
def dot(v: T): Double
def norm: Double = math.sqrt(this dot this)
def dist(v: T) = (this - v).norm
}
如果没有自我类型,则无法调用this.dot(this)
,因为dot
期望T
;因此我们需要使用注释强制执行它。
另一方面,如果没有CRTP,我们将无法在norm
上致电(this - v)
,因为-
会返回T
,因此我们需要确保类型T
具有此方法,例如声明T
是 Vec[T]
。
答案 2 :(得分:4)
我不确定正确的Scala语法,但您可以实现CRTP,即通过通用参数定义实际类型。
trait Vec[V <: Vec[V]] {
def +(v:V):V
...
}
class Vec2D extends Vec[Vec2D] { }
class Vec3D extends Vec[Vec3D] { }
class Polygon[V <: Vec[V]] {
...
}
答案 3 :(得分:2)
在JVM上使用具有CRTP模式的共同祖先存在一个大问题。当您使用不同的实现执行相同的抽象代码时,JVM将对代码进行去优化(无内联+虚拟调用)。如果您只使用Vec3D进行测试,您将不会注意到这一点,但如果您使用Vec2D和Vec3D进行测试,您将看到性能大幅下降。此外,Escape Analysis不能应用于去优化代码(没有标量替换,没有新实例的消除)。 缺少这些优化会使您的程序减慢3倍(非常全面的猜测取决于您的代码)。
尝试一些运行约10秒的基准测试。在与Vec2D相同的运行测试中,然后是Vec3D,然后是Vec2D,然后是Vec3D。你会看到这种模式: