右关联扩展方法:详细信息
扩展方法可以具有的最通用的签名如下
- 可选类型子句
leftTyParams
- 可能为空的 using 子句列表
leadingUsing
- 单个参数
leftParam
(在显式 term 子句中) - 一个可能为空的
trailingUsing
使用子句列表 - 一个名称(以
def
关键字开头) - 一个可选的类型子句
rightTyParams
- 一个可选的单个参数
rightParam
(在显式项子句中) - 任意数量的任意子句
rest
例如
extension [T] // <-- leftTyParams
(using a: A, b: B)(using c: C) // <-- leadingUsing
(x: X) // <-- leftParam
(using d: D) // <-- trailingUsing
def +:: [U] // <-- rightTyParams
(y: Y) // <-- rightParam
(using e: E)(z: Z) // <-- rest
如果扩展方法以 :
结尾并紧跟一个显式项参数(换句话说,存在 rightParam
),则该方法将被视为右结合运算符(如 SLS §6.12.3 中所述)。在上面的示例中,该参数是 (y: Y)
。
如果 x
是一个纯表达式或按名称调用参数,Scala 编译器会将右结合中缀运算(如 x +: xs
)预处理为 xs.+:(x)
;否则,预处理为 val y = x; xs.+:(y)
。这是必需的,因为常规的右结合中缀方法是在其右操作数的类中定义的。为了弥补这种交换,右结合扩展方法的展开执行相反的参数交换。更准确地说,如果 rightParam
存在,则扩展方法展开的总参数序列为
leftTyParams leadingUsing rightTyParams rightParam leftParam trailingUsing rest
换句话说,我们将 leftParams trailingUsing
与 rightTyParam rightParam
交换。
例如,上面的 +::
方法将变为
<extension> def +:: [T]
(using a: A, b: B)(using c: C)
[U]
(y: Y)
(x: X)
(using d: D)
(using e: E)(z: Z)
在编写具有参数间依赖关系的右结合扩展方法时,必须记住此展开。
此展开在以非中缀形式调用扩展方法时还会引入一些不一致性。用户需要手动反转调用位置的参数顺序。例如
extension [T](x: T)
def *:(xs: List[T]): List[T] = ...
y.*:(ys) // error when following the parameter definition order
ys.*:(y)
*:(y)(ys) // error when following the parameter definition order
*:(ys)(y)
此表示形式的另一个限制是无法显式传递 def
的类型参数(除非以前缀形式调用)。例如
extension (x: Int)
def *:[T](xs: List[T]): List[T] = ...
xs.*:[Int](1) // error when trying to set T explicitly
右结合扩展方法的展开也会影响可以显式传递上下文参数的顺序。
组扩展的行为也可能不直观,通常组中的所有扩展都是对接收者的扩展。除非其中一个扩展是右结合扩展方法,在这种情况下,该扩展是对其参数类型的扩展。例如
extension (a: Int)
def :+(b: Long): Long = ... // extension on Int
def +:(b: Long): Long = ... // extension on Long