1

クラスIconのフィールドposがクラスElementposフィールドを隠しているように見えるこのバグを見つけるのに非常に苦労しまし

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 を抽象化する: これにより、Element に新しい pos を設定できなくなります
  • コンストラクタ パラメータの名前変更 (例: _pos): API が台無しになるため、これは行いません。
  • 派生クラスで var をオーバーライドする: 可変変数をオーバーライドできないことをコンパイラから通知されるだけです。

抜け道は?

4

2 に答える 2

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)。

更新:待ってください、これはうまくいきません!私はそれをコンパイラのバグと呼んでいます。同じものであってはならない(IMHO)にもかかわらず、それは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 : これはコメントへの返信であり、別のコメントには収まりません。

ランドール・シュルツ 言います:

バグだとは思いません。

それは確かにバグのように見えます。少なくとも、私が根拠を知りたい矛盾です。

最初に注意すべきことは、上記の私の回避策では、self eq this. それらは実際にはまったく同じ参照を指しており、さらに同じ静的型を持っています。それでは、なぜそれが2つの異なるものself.posthis.pos返すのでしょうか(返す「正しい」ものが何であるかに関係なく)?つまり、selfは のエイリアスthisであり、エイリアスはすべて同じように動作する必要があります。

さて、 のコンストラクターへのパラメーターではなく、this.pos変数を示すべきだと私が思う理由は簡単です。このパラメーターは の前に付いていないため、実際には単なるパラメーターです ( ではありません)。そのため、レキシカルスコープのため、クラスでのみアクセスできます。これはのメンバーではなく、private のメンバーでもありません (内部で private フィールドが生成されるという事実は、言語のセマンティクスを変更しません)。パラメータがメンバーでない場合、その理由はありません。ElementIconvalvalIconIconposthis.pos返すべきです。明らかに、この引数は、パラメーターがクラスのメンバーでもあるかどうかに要約されます。私には明らかにそうではありませんが、実際に自動的にメンバーでもあると想定されている場合(これについての言及についてはまだ仕様を調べています)self.pos、現在のパラメーターの代わりにパラメーターの値を返すことは確かに論理的です基本クラスの var の値 (これはまだ方法self.posを説明しておらずthis.pos、別のことを意味する可能性があります)。

これが、自己型 ((通常は) これ以外の名前を使用する制約のない種類) が存在する理由の 1 つです。

うーん、ダメ。自己型は関係ありません。制約のない自己型はエイリアスを導入するだけなので、エイリアスは と同じ参照を指します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 =>

しかし、それは何も変わりません。

于 2013-03-05T23:11:32.977 に答える
0

コンストラクターのパラメーターはクラス本体に表示されます。これがまさにそのように機能します。private val で public 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)
    ...
  }
}

ただし、拡張するものにElementa を宣言するのではなく、値で初期化したいだけの場合は役に立ちません。私自身のアドバイスは、コンストラクターを避け、 のようなものを使用し、本体でを初期化することです。varElementvarinitialPosvar

于 2013-03-06T00:55:26.327 に答える