【发布时间】:2018-07-23 06:17:28
【问题描述】:
我正在为现有的 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 object、object、class 或 trait 中定义,而不是就在package。 (我不知道为什么这个限制是必要的,但我认为它是为了与隐式转换函数兼容。)
不过,我还从AnyVal 扩展了RichValue 以使其成为值类。如果您不熟悉它们,它们允许 Scala 编译器进行分配优化。具体来说,编译器并不总是需要创建RichValue的实例,可以直接对值类的构造函数参数进行操作。
换句话说,使用 Scala 隐式值类 作为包装器几乎没有性能开销,这很好。 :-)
然而,值类的一个主要限制是它们不能在class 或trait 中定义;他们只能是packages、package objects 或objects 的成员。 (这样他们就不需要维护指向外部类实例的指针了。)
隐式值类必须遵守两组约束,因此它只能在package 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 语句将所有内容纳入范围。
是否有一种直接的方法来实现这一点,而不会牺牲这种方法的任何好处?
(例如,我知道如果我放弃制作这些包装器值类,那么我可以在多个不同的traits 中定义它们 - 每个组件包一个 - 并拥有我的root package object 扩展所有这些,通过一次导入将所有内容纳入范围,但我不想为了方便而牺牲性能。)
【问题讨论】:
标签: scala value-class implicit-class