10

我正在为现有的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(前提是后者在范围内)。所以现在我可以将其中定义的方法应用RichValueValue. 例如:

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

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

不幸的是,Scala不允许implicit类是顶级的,因此它们必须在 a 、 或 中定义package object,而object不仅仅是在 a 中。(我不知道为什么这个限制是必要的,但我认为它是为了与隐式转换函数兼容。)classtraitpackage

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

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

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

隐式值类必须遵守两组约束,因此它只能在 apackage object或 an中定义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- 每个组件包一个 - 并让我的根package object扩展所有这些,通过单个将所有内容带入范围导入,但我不想为了方便而牺牲性能。)

4

1 回答 1

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
于 2018-02-13T06:31:04.850 回答