隐含值的优雅分组 类

Elegant grouping of implicit value classes

我正在为现有的 Java 库编写一组隐式 Scala 包装器 classes (这样我就可以装饰那个库,让Scala开发者更方便)。

作为一个简单的例子,假设 Java 库(我无法修改)有一个 class 如下所示:

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

现在假设我想用 Scala 风格的 getter 和 setter 装饰这个 class。我可以使用以下隐式 class 来做到这一点:

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 class 成为顶级,因此它们必须在 package object 中定义、objectclasstrait 而不仅仅是 package。 (我不知道为什么这个限制是必要的,但我认为这是为了与 隐式转换函数 兼容。)

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

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

然而,value classes 的一个主要限制是它们不能在 classtrait 中定义;他们只能是 packages、package objects 或 objects 的成员。 (这样他们就不需要维护指向外部 class 实例的指针。)

隐式值 class 必须遵守两组约束,因此它只能在 package objectobject 中定义.

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

import mylib.implicits._

尽可能简单地使用它们。

目前我能看到的实现此目的的唯一方法是将我所有的 隐式值 class 定义放在单个 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 语句将所有内容纳入范围。

有没有一种既不牺牲这种方法的任何好处又可以实现这一目标的直接方法?

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

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