【问题标题】:Genericizing Constructors in Scala在 Scala 中泛化构造函数
【发布时间】:2013-04-23 19:08:15
【问题描述】:

我一直在用 Scala 做一些练习。我想我可能会尝试使用新添加的AnyVal trait 来派生一种创建不兼容的值类型的方法,这些值类型不会被意外地分配给彼此。

我能想到的最好的方法是这样的:

object Measurements {
  trait ValueType[T] extends Any {
    def value: T
  }

  trait Measurement[A <: ValueType[Double]] extends Any {
    def modify(fn: (Double, A) => Double, value: A): A
    def +(mod: A) = modify((x: Double, y: A) => x + y.value, mod)
    def -(mod: A) = modify((x: Double, y: A) => x - y.value, mod)
    def *(mod: A) = modify((x: Double, y: A) => x * y.value, mod)
    def /(mod: A) = modify((x: Double, y: A) => x / y.value, mod)
  }

  case class Frequency(value: Double) extends AnyVal 
      with ValueType[Double] 
      with Measurement[Frequency] 
  {
    def modify(fn: (Double, Frequency) => Double, mod: Frequency) 
      = Frequency(fn(value, mod))
  }

  case class Amplitude(value: Double) extends AnyVal 
      with ValueType[Double] 
      with Measurement[Amplitude] 
  {
    def modify(fn: (Double, Amplitude) => Double, mod: Amplitude) 
      = Amplitude(fn(value, mod))
  }

  case class Wavelength(value: Double) extends AnyVal 
      with ValueType[Double] 
      with Measurement[Wavelength] 
  {
    def modify(fn: (Double, Wavelength) => Double, mod: Wavelength) 
      = Wavelength(fn(value, mod))
  }
}
import Measurements._
Frequency(150) + Frequency(10) // ==> Frequency(160)
Amplitude(23.2) * Amplitude(2) // ==> Amplitude(46.4)
Amplitude(50) + Frequency(50)  // ==> Compile-time Type Error

不幸的是,它要求我为每个实例唯一地定义modify 函数,因为不可能用泛型类型A 定义类似A(value) 的东西。似乎没有办法定义构造函数约束。否则我也许可以在 trait 上定义一些常见的东西,比如:

def modify(fn: (Double, A) => Double, mod: A) = A(fn(value, mod))

我已尝试在 A 上调用 apply(Double),但无法从通用变量访问它。我还试图看看我是否可以建立某种工厂以至少简化事情,但无法想出比我现在所做的更优雅的事情。我一直在用 C# 遇到同样的问题。

有没有办法将依赖于不同(但相关)类的通用构造函数类型的代码分解出来?

【问题讨论】:

标签: scala generics traits


【解决方案1】:

如果不求助于运行时反射(或者可能是宏),我认为这是不可能的。基本上有三个问题,你已经指出了其中两个:

  • 特征不可能声明强制构造函数签名。

  • 无法在A 上调用诸如apply 之类的方法,因为它是类型变量,而不是类或对象。

  • 由于值类(当前)只能扩展universal traits,但不能扩展抽象类,因此无法使用TypeTags 来获取属于A 实例化类型的类当Measurement 被扩展时。

我能想到的最好方法是以下方法。请注意,它使用反射,如果Measurement 的具体实例未声明适当的构造函数,则可能会在运行时引发异常。

// ... as above ...

trait Measurement[A <: ValueType[Double]] extends Any { self: A =>
  def modify(fn: (Double, A) => Double, mod: A): A =
    this.getClass
        .getConstructor(this.getClass)
        .newInstance(fn(value, mod): java.lang.Double)

  // ... as above ...
}

case class Frequency(value: Double)
    extends AnyVal 
       with ValueType[Double] 
       with Measurement[Frequency]

// ... etc ...

Frequency(150) + Frequency(10) // ==> Frequency(160)
Amplitude(23.2) * Amplitude(2) // ==> Amplitude(46.4)
Amplitude(50) + Frequency(50)  // ==> Compile-time Type Error

自类型注解self: A 确保ValueType 声明的value 字段可从内部特征Measurement 访问。

【讨论】:

  • 我有点担心是这种情况(任何人都花了两天时间才尝试回答)。反射解决方案确实有效,但通常以性能和安全为代价,这是一种耻辱。不过,作为参考还是不错的。 +1 w/接受
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-22
  • 1970-01-01
  • 2020-09-11
  • 2013-09-08
  • 2011-02-11
  • 1970-01-01
  • 2018-03-15
相关资源
最近更新 更多