如何在没有开销的情况下丰富价值类?

时间:2013-02-13 19:52:58

标签: scala enrich-my-library value-class

Scala 2.10引入了值类,您可以通过使类扩展AnyVal来指定它们。值类有许多限制,但它们的一大优点是它们允许扩展方法而不会产生新类的惩罚:除非需要装箱,例如要将值类放在数组中,它只是旧类加上一组将类作为第一个参数的方法。因此,

implicit class Foo(val i: Int) extends AnyVal {
  def +*(j: Int) = i + j*j
}

展开一些不比自己编写i + j*j更贵的东西(一旦JVM内联方法调用)。

不幸的是,SIP-15中描述值类的限制之一是

  
      
  1. C的基础类型可能不是值类。
  2.   

如果你有一个价值类,你可以把它作为一种提供类型安全单位而不需要拳击开销的方法(除非你真的需要它):

class Meter(val meters: Double) extends AnyVal {
  def centimeters = meters*100.0                // No longer type-safe
  def +(m: Meter) = new Meter(meters+m.meters)  // Only works with Meter!
}

那么有没有办法在没有对象创建开销的情况下丰富Meter? SIP-15中的限制阻止了明显的

implicit class RichMeter(m: Meter) extends AnyVal { ... }

方法

1 个答案:

答案 0 :(得分:14)

为了扩展值类,您需要重新获得基础类型。由于要求值类可以访问其包装类型(val i而不仅仅是i),因此您始终可以执行此操作。您无法使用方便的implicit class快捷方式,但仍可以手动添加隐式转换。因此,如果您要向-添加Meter方法,则必须执行类似

的操作
class RichMeter(val meters: Double) extends AnyVal {
  def -(m: Meter) = new Meter(meters - m.meters)
}
implicit def EnrichMeters(m: Meter) = new RichMeter(m.meters)

另请注意,您可以(自由地)使用原始值类重新包装任何参数,因此如果它具有您依赖的功能(例如,它包装Long但执行复杂的位混合),则可以只在你想要扩展的值类中重新包装底层类。

(另请注意,除非import language.implicitConversions,否则会收到警告。)

附录:在Scala 2.11+中,您可以将val设为私有;对于完成此操作的情况,您将无法使用此技巧。