现在我有RealVector
班和ComplexVector
班。它们的逻辑几乎完全相同,所以我想将它们组合成一个Vector
类。 RealVector
需要List[Double]
而ComplexVector
需要List[ComplexNumber]
,其中ComplexNumber
是我创建的案例类。
如何制作,case class Vector
接受两种List
类型中的任何一种?请注意,虽然大多数方法的代码都相同,但有些方法可能会返回Double
或ComplexNumber
,具体取决于List
类型。在这个实例中使用case类是否正确,或者我应该使用普通类?
编辑:我当前的代码
trait VectorElement[A]
implicit object RealVectorElement extends VectorElement[Double]
implicit object ComplexVectorElement extends VectorElement[ComplexNumber]
case class MyVector[A: VectorElement](components: List[A]) {
def +(that:MyVector[A]):MyVector[A] = {
if (this.dimension != that.dimension) throw new Exception("Cannot add MyVectors of different dimensions.");
new MyVector((this.components zip that.components).map(c => c._1 + c._2));
}
def -(that:MyVector[A]):MyVector[A] = {
if (this.dimension != that.dimension) throw new Exception("Cannot subtract MyVectors of different dimensions.");
new MyVector((this.components zip that.components).map(c => c._1 - c._2)); // ERROR HERE: error: value - is not a member of type parameter A
}
...
}
答案 0 :(得分:2)
最熟悉的方法是创建两个Vector类的通用超类型
abstract class Vector[A](elements: List[A]){
//common code
}
case class RealVector(elements: List[Double]) extends Vector[Double](elements)
case class ComplexVector(elements: List[ComplexNumber]) extends Vector[ComplexNumber](elements)
如果您只想要一个类型可以将Generics与案例类一起使用,那么case class Vector[A](values: List[A])
就可以了。
现在这将允许任何类型的List,所以让我们缩小范围。如果我们处理自定义类型,我们可以使用特征中的常见超类型,但Double
是内置的,我们无法对其进行修改。
我们可以做的是使用类型类,这是scala中可能的灵活多态形式。我们可以使用特征定义类型类。
trait VectorElement[A]
如果我们只是想用它来标记我们想要的类型,那么我们也可以将我们需要的常用功能放在这里。
如果我们将案例类定义修改为
case class Vector[A: VectorElement](values: List[A])
我们将泛型类型仅限于具有VectorElement实例的那些类型。上面的代码是case class Vector[A](values: List[A])(implicit ev: VectorElement[A])
我们现在可以为我们需要的类型创建实例
implicit object RealVectorElement extends VectorElement[Double]
implicit object ComplexVectorElement extends VectorElement[ComplexNumber]
现在,只要这两个隐式对象在范围内,我们可以将这些类型与Vector
类一起使用,但不能使用其他类型。
一些不相关的建议:
Vector
已经是标准库中的一个类,它总是自动导入,这有可能导致问题
List
可能不是最好的集合类型,因为它需要遍历集合以访问它的元素。可能你想选择更通用的东西,你可能想要很好的索引访问。如果您使用IndexedSeq
而不是List
作为集合类型,则可以确保使用具有良好的基于索引的随机访问权限的集合,例如Array
或Vector
(来自标准库的那个。)
答案 1 :(得分:1)
您可以尝试使用一些更高级的Scala类型系统功能:
object Types {
trait inv[-A] {}
type Or[A, B] = {
type check[X] = (inv[A] with inv[B]) <:< inv[X]
}
}
case class Vector[U : (Double Or Int)#check](list: List[U]) {
def first(): U = list.head
}
我在这里使用过Double
和Int
,但可以使用任何类型。用法很简单:
println(Vector(List(1.0, 2.0, 3.0)).first()) // prints a Double
println(Vector(List(1, 2, 3)).first()) // prints an Int
//Vector(List("String")).first() // won't compile