Scala子类字段仅在成员函数内隐藏超类var

时间:2013-03-05 22:55:20

标签: class scala abstract-class

我很难找到这个错误的地方 pos Icon 隐藏类元素 pos 字段,仅在 draw 函数中。

case class Vector2(val x: Float, val y: Float) { ... }

abstract class Element(var pos: Vector2) {
    def draw(): Unit
}

class Icon(pos: Vector2, var texture: String) extends Element(pos) {
  override def draw() {
    ...
    GL11.glTranslatef(pos.x, pos.y, 0f)
    ...
  }
}

后来:

// Create an icon with an initial position
val icon = new Icon(pos = Vector2(40,20), "crosshair")

// Draw all elements
elements.foreach{_.draw()} // => draws icon at (40,20)

// Setting a new position for icon
icon.pos = Vector2(100,200)

// See if it worked
Log.info(icon.pos.toString()) // => this prints Vector2(100,200)

// Draw all elements
elements.foreach{_.draw()} // => still draws icon at (40,20)

我见过this post,我试过了:

  • 在基类中创建var摘要:这可以防止我将新的pos设置为元素
  • 重命名构造函数参数(例如_pos):我不会这样做,因为这会搞砸API
  • 覆盖派生类中的var:仅由编译器告知我无法覆盖可变变量

出路是什么?

2 个答案:

答案 0 :(得分:2)

明确取消引用this

class Icon(pos: Vector2, var texture: String) extends Element(pos) {
  override def draw() {
    ...
    GL11.glTranslatef(this.pos.x, this.pos.y, 0f)
    ...
  }
}

鉴于阴影只发生在Icon内,其他地方(包含在派生类中),您可以继续使用pos(不需要this.pos)。

更新:没有等待,这不起作用!我称之为编译器错误。似乎this.pos被视为pos,即使他们不应该(恕我直言)是同一件事。 但是有一个简单的解决方法:

class Icon(pos: Vector2) extends Element(pos) {
  private def self = this
  override def draw() {
    println(self.pos.x, self.pos.y, 0f)
  }
}

更新2 :这是对评论的回复,不适合其他评论。

Randall Schulz说:

  

我不相信这是一个错误。

嗯,它肯定看起来像是一个错误,或者至少是一个我想要有理由的不一致性。

首先要注意的是,在我上面的工作中self eq this。它们确实指向了相同的引用,并且还具有相同的静态类型。那么为什么self.posthis.pos会返回两个不同的东西(不管返回的“正确”的东西是什么)?换句话说,selfthis的别名,别名的行为必须都是相同的。

现在,我认为this.pos应该表示Element中的变量而不是Icon的构造函数的参数的原因很简单。此参数前面没有val,因此实际上只是一个参数(不是val)。因此,只有因为词法作用域才能在类Icon中访问它。 Icon的成员,甚至不是私有成员(生成私有字段的事实并不会改变语言的语义)。如果pos参数不是成员,则this.pos没有理由返回它。 显然,这个论点归结为参数是否也是该类的成员。对我来说显然不是,但如果实际上它应该自动也是一个成员(我仍然在查看规范中提到这一点)那么self.pos返回值的确合乎逻辑。参数而不是基类中var的当前值(这仍然不能解释self.posthis.pos如何表示不同的东西)。

  

这就是存在自我类型(使用除此之外的名称的(通常)不受约束的变种)的原因之一。

嗯,不。自我类型是无关紧要的。不受约束的自我类型只引入别名,因此别名指向与this相同的引用(如果不受约束,则具有相同的静态类型)。所以使用别名应该不会改变返回的内容。 实际上,它没有:

class Icon(pos: Vector2) extends Element(pos) { self =>
  override def draw() {
    println(self.pos.x, self.pos.y, 0f)
  }
}
val icon = new Icon(Vector2(1, 2))
icon.draw() // prints "(1.0,2.0,0.0)" as expected
icon.pos = Vector2(3, 4)
icon.draw() // oops, still prints "(1.0,2.0,0.0)"!

正如您所看到的,自我类型没有帮助:self.pos仍然指向参数而不是变量。 要解决此问题,您可能会尝试明确地将self键入为Element

class Icon(pos: Vector2) extends Element(pos) { self: Element =>

但它没有改变任何东西。

答案 1 :(得分:0)

构造函数参数在类体中是可见的,这就是事物的工作方式。私有val影子是一个公共var是很奇怪的,我授予你,但就是这样。在您展示的特定代码段中,您可以执行以下操作:

abstract class Element {
  def pos: Vector2
  def pos_=(x: Vector2): Unit
  def draw(): Unit
}

然后

class Icon(var pos: Vector2, var texture: String) extends Element {
  override def draw() {
    ...
    GL11.glTranslatef(pos.x, pos.y, 0f)
    ...
  }
}

但是,如果您只想使用值初始化Element,而不是在扩展var的任何内容上声明Element,则无效。我自己的建议是避免var构造函数,使用initialPos之类的东西,并初始化正文中的var