如何重载我的case类构造函数以允许两种不同的类型?

时间:2017-08-24 17:52:56

标签: scala constructor-overloading

现在我有RealVector班和ComplexVector班。它们的逻辑几乎完全相同,所以我想将它们组合成一个Vector类。 RealVector需要List[Double]ComplexVector需要List[ComplexNumber],其中ComplexNumber是我创建的案例类。

如何制作,case class Vector接受两种List类型中的任何一种?请注意,虽然大多数方法的代码都相同,但有些方法可能会返回DoubleComplexNumber,具体取决于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
    }
    ...
}

2 个答案:

答案 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作为集合类型,则可以确保使用具有良好的基于​​索引的随机访问权限的集合,例如ArrayVector(来自标准库的那个。)

答案 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
}

我在这里使用过DoubleInt,但可以使用任何类型。用法很简单:

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