是否可以指定类型在其构造函数中具有某些参数?斯卡拉

时间:2019-01-16 14:41:59

标签: scala

所以我有一个假定使用通用类型的类,但是该类型应该具有某些特征

  1. 它需要具有定义的方法calculate
  2. 它需要具有接受Seq[Double]
  3. 的构造函数

此刻我有个特征

trait HasCalculate {def calculate(): Double}

我以这种方式使用它:

val pars = Seq(1.0, 2.0)
val calc = new Calc1(pars) with HasCalculate
val res = calc.calculate

当我想使用另一个计算器时,将Calc2代替Calc1放在类的代码中。但是我想用通用的方式来做,就像这样:

class MyClass[T]{
 val pars = Seq(1.0, 2.0)
 val calc = new T(pars) with HasCalculate
 val res = calc.calculate
}

但是如何定义T具有接受Seq[Double]的构造函数?

2 个答案:

答案 0 :(得分:4)

您所描述的内容听起来好像在Scala中是不可能的(它实际上没有用于构造函数抽象的功能),并且在没有更具体地了解您的较大目标的情况下,很难提供良好的建议,但是以下是一种Scala惯用的解决方案,提供MyClass所需要的使用方式,并且经过专门设计,可让您使用通用类型,同时将这些类型限制为具有某些操作。

第一步是编写一个 type类来捕获所需的操作:

trait Calculable[A] {
  def create(values: Seq[Double]): A
  def calculate(a: A): Double
}

您可以将此类型的实例视为“证据”,可以对某些A执行这些操作。

接下来,您将像这样写MyClass

class MyClass[T: Calculable] {
  private val instance = implicitly[Calculable[T]]
  val pars = Seq(1.0, 2.0)
  val calc: T = instance.create(pars)
  val res: Double = instance.calculate(calc)
}

T: Calculable部分是一个“上下文绑定”,它指定必须有隐式证据证明T具有一个Calculable实例。这是一个约束条件,规定“ T可以是任何类型,只要我们知道如何对其执行Calculable操作。”

现在,您可以像这样编写一个可以用作T的特定类:

class MyCalculation(vs: Seq[Double]) {
  def calculate(): Double = vs.sum
}

object MyCalculation {
  implicit val calculableInstance: Calculable[MyCalculation] =
    new Calculable[MyCalculation] {
      def create(values: Seq[Double]): MyCalculation = new MyCalculation(values)
      def calculate(a: MyCalculation): Double = a.calculate()
    }
}

您将获得所需的用法:

scala> val myClass = new MyClass[MyCalculation]
myClass: MyClass[MyCalculation] = MyClass@646bf8a6

scala> myClass.res
res0: Double = 3.0

如果您控制MyCalculation的定义,则最方便地定义其隐式Calculable[MyCalculation]的地方是MyCalculation伴随对象,但是类型类方法的优点之一是:它将类型操作的定义与类型的定义分开,这些实例可以分别定义。

答案 1 :(得分:0)

我想自己分享一个答案...

因此,MyClass具有类型参数,它可以具有函数作为参数,如下所示:

class MyClass(f:(Seq[Double])=>HasCalculate){
 val pars = Seq(1.0, 2.0)
 val calc = f(pars)
 val res = calc.calculate
}

然后在其主体中提供一个带有构造函数的匿名函数:

val myClass = new MyClass((s:Seq[Double])=>new Calc1(s) with HasCalculate)

当然这看起来很丑陋,但就我而言,它似乎比Travis的解决方案更实用,因为我有很多计算器,并且我不打算为每个计算器或每次运行时都创建该工厂对象通过MyClass的计算器。我只是复制这一行代码,然后将Calc1替换为Calc99 ...

因此,如果您只有很少的计算器和对MyClass的大量调用,那么肯定是Trevis的解决方案更好,否则可能会有用...