Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Class Scala子类字段仅在成员函数中隐藏超类变量_Class_Scala_Abstract Class - Fatal编程技术网

Class Scala子类字段仅在成员函数中隐藏超类变量

Class Scala子类字段仅在成员函数中隐藏超类变量,class,scala,abstract-class,Class,Scala,Abstract Class,我很难找到这个bug,因为类图标的pos字段似乎隐藏了类元素的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

我很难找到这个bug,因为类图标的pos字段似乎隐藏了类元素的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)
我见过,也试过:

  • 在基类中抽象var:这会阻止我为元素设置新的pos
  • 重命名构造函数参数(例如_pos):我不会这么做,因为这会破坏API
  • 重写派生类中的变量:只会被编译器告知我不能重写可变变量

出路是什么?

只需明确地取消引用

class Icon(pos: Vector2, var texture: String) extends Element(pos) {
  override def draw() {
    ...
    GL11.glTranslatef(this.pos.x, this.pos.y, 0f)
    ...
  }
}
由于阴影只发生在
图标
内部,因此在其他任何地方(包括在派生类中),您都可以继续使用
pos
(不需要
this.pos

更新:不要等待,这不起作用!我称之为编译器错误。看来,
this.pos
被视为仅仅是
pos
,尽管它们不应该(IMHO)是相同的东西。 不过,有一个简单的解决方法:

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

更新2:这是对某条评论的回复,该回复不适用于其他评论

兰德尔·舒尔茨说:

我不相信这是虫子

当然,这看起来像是一个bug,或者至少是一个不一致性,我想对此有一个解释

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

现在,我认为
this.pos
应该表示
元素
中的变量,而不是
图标
构造函数的参数的原因很简单。此参数前面没有
val
,因此实际上只是一个参数(不是
val
)。因此,只能通过词法作用域在类
Icon
中访问它。它不是
图标的成员,甚至不是私有的(在引擎盖下生成私有字段的事实不会改变语言的语义)。如果
pos
参数不是成员,那么
this.pos
没有理由返回它。 显然,这个参数归结为参数是否也是类的成员。对我来说,它显然不是,但如果事实上它应该自动也是一个成员(我仍然在规范中寻找任何关于这一点的提及),那么
self.pos
返回参数值而不是基类中var的当前值确实是合乎逻辑的(这仍然无法解释
self.pos
this.pos
如何意味着不同的事情)

这就是self类型(通常是使用其他名称的无约束类型)存在的原因之一

没有。self类型是不相关的。不受约束的self类型只引入一个别名,因此别名指向与
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类型没有帮助:
self.pos
仍然指向参数而不是变量。 要解决此问题,您可能会尝试将
self
显式键入为
元素

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

但它不会改变任何东西。

构造函数参数在类主体中是可见的,这就是工作原理。我承认,将私有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)
    ...
  }
}

但是如果你只是想用一个值来初始化
元素,而不是在任何扩展
元素的
上声明一个
var
,那就没用了。我自己的建议是避免构造函数上的
var
,使用类似
initialPos
的东西,然后在主体中初始化
var

是的,现在这就行了。是否应该有人填写一个错误?谢谢!是的,一个错误报告似乎是有道理的。想这样做吗?至少,如果这是预期的行为,我们会知道它并可能得到一个理由。我不相信这是一个错误。这是自我类型的原因之一(通常是使用
以外的名称的无约束类型)exist.Self-types是不相关的。因为我不能在评论中做出正确的回答,所以我在我的回答中做了一个更新。啊,我明白为什么你认为这是一个bug…你认为它只是一个参数,而不是一个字段!嗯,不是真的——如果你在主体中引用它,而不是参数到超类或
val
初始化,它将被“提升”到私有字段。这包括将其称为
this.pos
。请注意,在构造函数主体(即方法主体内部)之外引用(非属性,即非
val
,非
var
)构造函数参数将强制编译器将该构造函数参数复制到(私有)中字段。因此实例大小可以微妙地增加。@Daniel C.Sobral:我认为
initialPos
解决方案的主要问题是您正在修改API:现在,当使用命名参数实例化
图标时,我们必须使用丑陋的名称
initialPos
插入
pos
ate val。这里没有私有val,只有一个可以通过简单的词法作用域访问的参数。至少我一直都是这样看的,如果规范没有这样说,我希望有一个指针。@RégisJean Gilles引用到b的任何构造函数参数