优化的隐式值类分组

Mik*_*len 10 scala value-class implicit-class

我正在为现有的Java库编写一组隐式Scala包装类(这样我就可以装饰该库以使Scala开发人员更方便).

作为一个简单的例子,让我们说Java库(我无法修改)有一个类,如下所示:

public class Value<T> {
    // Etc.
    public void setValue(T newValue) {...}
    public T getValue() {...}
}
Run Code Online (Sandbox Code Playgroud)

现在让我们说我想用Scala风格的getter和setter 来装饰这个类.我可以使用以下隐式类来完成此操作:

final implicit class RichValue[T](private val v: Value[T])
extends AnyVal {
  // Etc.
  def value: T = v.getValue
  def value_=(newValue: T): Unit = v.setValue(newValue)
}
Run Code Online (Sandbox Code Playgroud)

implicit关键字告诉Scala编译器它可以将实例转换ValueRichValue隐式实例(前提是后者在范围内).所以现在我可以将其中定义的方法RichValue应用于实例Value.例如:

def increment(v: Value[Int]): Unit = {
  v.value = v.value + 1
}
Run Code Online (Sandbox Code Playgroud)

(同意,这不是很好的代码,并不完全正常.我只是想演示一个简单的用例.)

不幸的是,斯卡拉不允许implicit类是顶层,所以他们必须在一个定义package object,object,classtrait不只是在package.(我不知道为什么这个限制是必要的,但我认为这是为了与隐式转换函数兼容.)

但是,我也正在扩展RichValue,AnyVal使其成为一个价值类.如果您不熟悉它们,它们允许Scala编译器进行分配优化.具体来说,编译器并不总是需要创建实例RichValue,并且可以直接在值类的构造函数参数上操作.

换句话说,使用Scala隐式值类作为包装器的性能开销很小,这很好.:-)

但是,价值等级的一个主要限制是它们不能在a class或a中定义trait; 他们只能是packages,package objects或objects的成员.(这样它们就不需要维护指向外部类实例的指针.)

隐含值类必须履行这两组的限制,因此它可以仅在被定义package objectobject.

其中存在问题.我正在包装的库包含一个包含大量类和接口的深层包.理想情况下,我希望能够使用单个import语句导入我的包装类,例如:

import mylib.implicits._
Run Code Online (Sandbox Code Playgroud)

使它们尽可能简单.

我目前可以看到实现这一目标的唯一方法是将所有隐式值类定义放在单个源文件中的单个package object(或object)中:

package mylib
package object implicits {

  implicit final class RichValue[T](private val v: Value[T])
  extends AnyVal {
    // ...
  }

  // Etc. with hundreds of other such classes.
}
Run Code Online (Sandbox Code Playgroud)

然而,这远非理想,我更愿意镜像目标库的包结构,但仍然通过单个import语句将所有内容都纳入范围.

是否有一种直接的方法来实现这一点,不会牺牲这种方法的任何好处?

(例如,我知道如果我放弃制作这些包装器值类,那么我可以在许多不同的traits中定义它们- 每个组件包一个 - 并让我的根package object扩展所有它们,通过一个将所有内容放入范围导入,但我不想为了方便而牺牲性能.)

Ole*_*cov 5

implicit final class RichValue[T](private val v: Value[T]) extends AnyVal
Run Code Online (Sandbox Code Playgroud)

基本上是以下两个定义的语法糖

import scala.language.implicitConversions // or use a compiler flag

final class RichValue[T](private val v: Value[T]) extends AnyVal
@inline implicit def RichValue[T](v: Value[T]): RichValue[T] = new RichValue(v)
Run Code Online (Sandbox Code Playgroud)

(你可能会看到,这就是隐式类必须在traits,object或classes中的原因:它们也有匹配def)

没有什么要求这两个定义共存.您可以将它们放入单独的对象中:

object wrappedLibValues {
  final class RichValue[T](private val v: Value[T]) extends AnyVal {
    // lots of implementation code here
  }
}

object implicits {
  @inline implicit def RichValue[T](v: Value[T]): wrappedLibValues.RichValue[T] = new wrappedLibValues.RichValue(v)
}
Run Code Online (Sandbox Code Playgroud)

或进入特质:

object wrappedLibValues {
  final class RichValue[T](private val v: Value[T]) extends AnyVal {
    // implementation here
  }

  trait Conversions {
    @inline implicit def RichValue[T](v: Value[T]): RichValue[T] = new RichValue(v)
  }
}

object implicits extends wrappedLibValues.Conversions
Run Code Online (Sandbox Code Playgroud)