在 GitHub 上编辑此页面

特征参数

Scala 3 允许特征具有参数,就像类具有参数一样。

trait Greeting(val name: String):
  def msg = s"How are you, $name"

class C extends Greeting("Bob"):
  println(msg)

在初始化特征之前立即计算特征的参数。

特征参数的一个潜在问题是如何防止歧义。例如,您可能尝试使用不同的参数扩展 Greeting 两次。

class D extends C, Greeting("Bill") // error: parameter passed twice

这应该打印“Bob”还是“Bill”?事实上,此程序是非法的,因为它违反了以下特征参数的第二个规则

  1. 如果类 C 扩展了参数化特征 T,并且其超类没有扩展,则 C 必须T 传递参数。

  2. 如果类 C 扩展了参数化特征 T,并且其超类也扩展了,则 C 不能T 传递参数。

  3. 特征绝不能向父特征传递参数。

这是一个扩展参数化特征 Greeting 的特征。

trait FormalGreeting extends Greeting:
  override def msg = s"How do you do, $name"

根据要求,不会向 Greeting 传递任何参数。然而,在定义一个扩展 FormalGreeting 的类时,这会带来一个问题

class E extends FormalGreeting // error: missing arguments for `Greeting`.

编写 E 的正确方法是同时扩展 GreetingFormalGreeting(顺序不限)

class E extends Greeting("Bob"), FormalGreeting

具有上下文参数的特征

如果缺失的特征仅包含 上下文参数,则会放宽此“需要显式扩展”规则。在这种情况下,特征引用会作为具有推断参数的附加父级隐式插入。例如,下面是问候语的一个变体,其中收件人是类型为 ImpliedName 的上下文参数

case class ImpliedName(name: String):
  override def toString = name

trait ImpliedGreeting(using val iname: ImpliedName):
  def msg = s"How are you, $iname"

trait ImpliedFormalGreeting extends ImpliedGreeting:
  override def msg = s"How do you do, $iname"

class F(using iname: ImpliedName) extends ImpliedFormalGreeting

最后一行中 F 的定义会隐式扩展为

class F(using iname: ImpliedName) extends
  Object,
  ImpliedGreeting(using iname),
  ImpliedFormalGreeting

请注意插入的超级特征 ImpliedGreeting 的引用,该引用未明确提及。

参考

有关更多信息,请参阅 Scala SIP 25