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