优化的隐式值类分组

时间:2018-02-12 23:33:46

标签: scala value-class implicit-class

我正在为现有的 Java 库编写一组隐式 Scala 包装类(这样我就可以装饰该库来制作它对 Scala 开发人员来说更方便。

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

public class Value<T> {
    // Etc.
    public void setValue(T newValue) {...}
    public T getValue() {...}
}

现在让我们说我想用 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)
}

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

def increment(v: Value[Int]): Unit = {
  v.value = v.value + 1
}

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

不幸的是, Scala 不允许implicit个类位于顶级,因此必须在package objectobject,{{1}内定义它们}或class而不仅仅是trait。 (我不知道为什么这个限制是必要的,但我认为这是为了与隐式转换函数的兼容性。)

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

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

但是,值类的一个主要限制是它们无法在RichValueclass中定义;他们只能是traitpackagepackage object的成员。 (这样它们就不需要维护指向外部类实例的指针。)

隐式值类必须遵守这两组约束,因此只能在objectpackage object内定义。

这就是问题所在。我正在包装的库包含一个包含大量类和接口的深层包。理想情况下,我希望能够使用单个object语句导入我的包装类,例如:

import

尽可能简单地使用它们。

我目前可以看到实现此目的的唯一方法是将所有隐式值类定义放在单个源中的单个import mylib.implicits._ (或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. } 语句将所有内容都纳入范围。

是否有一种直接的方法可以实现这一目标,而不会牺牲这种方法的任何好处?

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

1 个答案:

答案 0 :(得分:5)

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

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

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)

(你可能会看到,这就是为什么隐式类必须在特征,对象或类中:它们也有匹配的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)
}

或进入特质:

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