【问题标题】:Overriding a repeated class parameter in Scala?在Scala中覆盖重复的类参数?
【发布时间】:2011-07-27 14:40:58
【问题描述】:

Scala 语言规范 2.8 版的第 4.6.2 节描述了重复的参数并说:

参数部分的最后一个值参数可以用“*”作为后缀,例如(..., x:T*)。方法内部的这种 repeated 参数的类型就是序列类型 scala.Seq[T]。

但是,这段代码:

abstract class A { def aSeq : Seq[A] }
class B(val aSeq : A*) extends A
class C extends B { override val aSeq :Seq[A] = Seq() }

编译时报错:

overriding value aSeq in class B of type A*;  value aSeq has incompatible type

编译器似乎表明 A* 是与 Seq[A] 不同的类型。

在这种情况下调查 aSeq 的实际类表明它是 scala.collection.mutable.WrappedArray$ofRef 的实例,但即使以下代码也无法编译并显示相同的消息: p>

class C extends B { override val aSeq  = new ofRef(Array[A]()) }

所以问题是,我如何去覆盖由类上的重复参数定义的成员?

如果你想知道这是从哪里来的,这正是 scala.xml.Elem 所做的,它覆盖了 scala.xml.Node 中的 child 方法。

【问题讨论】:

  • 我建议不要在类中使用重复参数作为vals(我有点惊讶它可以编译)。取而代之的是 class B(val aSeq: Seq[A]) extends A,如果您想使用语法 B(a1, a2, ...),请将其添加到伴随对象中:object B {def apply(aSeq : A*) = new B(aSeq)}
  • 不是我的代码在使用它。正如我在帖子末尾所说,重复的参数由 scala.xml.Node 的 scala.xml.Elem 子类使用。我没有更改它的选项。
  • 啊,我看错了。刚刚检查了scala.xml.Node 而不是scala.xml.Elem
  • 我试过override val seq: Int* = Seq(1,2)override val seq = Seq(1,2): _*。不幸的是,两者都不起作用。我怀疑这可能是不可能的,但希望这是错误的。

标签: scala


【解决方案1】:

您的问题可以概括为:

scala> class A { def aSeq(i: Int*) = 1 }
defined class A

scala> class B extends A { override def aSeq(i: Seq[Int]) = 2 }
<console>:6: error: method aSeq overrides nothing
       class B extends A { override def aSeq(i: Seq[Int]) = 2 }

这些方法有不同的类型。规范说(强调我的):

这种重复参数的类型inside方法就是那么序列类型scala.Seq[T]

由于Int*Seq[Int] 不在方法中,因此该特定语句不适用。

有趣的是,下面这段代码表明方法在擦除之前具有不同的类型,但在擦除之后是相同的:

scala> class G { def aSeq(i:Int*) = 1; def aSeq(i:Seq[Int]) = 2 }
<console>:5: error: double definition:
method aSeq:(i: Seq[Int])Int and
method aSeq:(i: Int*)Int at line 5
have same type after erasure: (i: Seq)Int
       class G { def aSeq(i:Int*) = 1; def aSeq(i:Seq[Int]) = 2 }

那么问题就变成了,为什么你的B 类可以扩展你的A 抽象类。那里的规范可能存在不一致。我不知道...

编辑:我重新阅读了规范,但我无法弄清楚是否有任何与重复参数和覆盖有关的内容。似乎没有关于重复参数的返回类型的任何内容,这是您通过 val aSeq 访问器方法获得的。

我认为马克的回答是一个完全有效的方法。如果您无法遵循它,您可以使用以下解决方法:

class C extends B {
  private def aSeqHelper(a: A*) = a
  override val aSeq = aSeqHelper(Seq[A](): _*)
}

例如:

import scala.xml._
class ElemX extends Elem("pref", "label", <xml a="b"/>.attributes, TopScope) {
  private def childHelper(c: Node*) = c
  override val child = childHelper(<foo/><bar/>: _*) }

然后:

scala> new ElemX
res4: ElemX = <pref:label a="b"><foo></foo><bar></bar></pref:label>

【讨论】:

  • 我在语言规范中找不到任何关于在类参数中使用重复参数的内容,更不用说当参数是 val 时结果成员的类型是什么。
  • 为什么它可以覆盖的最简单解释是编译器可能认为A*Seq[A] 的子类型(但不能在参数列表之外使用)。
  • 编译器似乎将其视为完全不同的类型。
  • 成功了!仍然不确定 childHelper 的真正类型是什么,但这是使其工作的优雅技巧。非常感谢。 Mark 的评论中的解决方案也不错,但是在这种情况下我不能使用它,因为我需要计算 child 的值。
  • 实际上,根据 REPL,childHelper 的类型是 (Node*) =&gt; Node*。返回类型无法用语法表示,但编译器很乐意推断它。
【解决方案2】:

xml.Elem的复制方法是这样使用的

def copy(
  prefix: String = this.prefix,
  label: String = this.label,
  attributes: MetaData = this.attributes,
  scope: NamespaceBinding = this.scope,
  child: Seq[Node] = this.child.toSeq
): Elem = Elem(prefix, label, attributes, scope, child: _*)

所以你可以覆盖B构造函数中的值

class C extends B(aSeq = Seq(): _*)

或者声明为C类的参数

class C(seq: Seq[A]) extends B(aSeq = seq: _*)

虽然我不确定它是否能回答你的问题!

【讨论】:

  • 这并不能满足我的需要。我需要能够在 C 中覆盖 B 中的 aSeq 成员,而不仅仅是在构造时初始化它的值。
【解决方案3】:

规范不允许重复类型以这种方式泄漏。编译器在 2011 年 7 月进行了更改以强制执行此操作。

更多背景信息,请查看票证中的 cmets:

https://issues.scala-lang.org/browse/SI-4176

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-05
    • 1970-01-01
    相关资源
    最近更新 更多