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

Rex*_*err 13 scala enrich-my-library value-class

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

implicit class Foo(val i: Int) extends AnyVal {
  def +*(j: Int) = i + j*j
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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!
}
Run Code Online (Sandbox Code Playgroud)

那么有没有办法在Meter没有对象创建开销的情况下进行充实?SIP-15的限制防止了明显的限制

implicit class RichMeter(m: Meter) extends AnyVal { ... }
Run Code Online (Sandbox Code Playgroud)

做法.

Rex*_*err 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)
Run Code Online (Sandbox Code Playgroud)

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

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

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