3
votes

I'm about to write a 2d game using LibGDX which has nice classes like Vector2.

Since I'm writing in functional fashion (copying, pattern matching, etc.) I'd like it to be a case class.

Because it's not one, I decided to try to extend it without success:

import com.badlogic.gdx

class Vector2(x: Float, y: Float) extends gdx.math.Vector2(x, y) {
  def copy(x: Float = this.x, y: Float = this.y): Vector2 = {
    new Vector2(x, y)
  }
}

object Vector2 {
  def apply(x: Float = 0, y: Float = 0): Vector2 = {
    new Vector2(x, y)
  }
}

The problem is that the following throws println(Vector2(1, 2).x) because there's no x field.

If I write it like class Vector2(override val x: Float, override val y: Float) it throws:

overriding variable x in class Vector2 of type Float; value x has incompatible type class Vector2(override val x: Float, override val y: Float) extends gdx.math.Vector2(x, y) {

I'm unable to get this to work. I also tried var instead and java.lang.Float without success.

Edit: It seems that it's not possible, because Scala generators a function and there's no equivalent function in Java code.

2

2 Answers

2
votes

You are shadowing the x you want.

People are often annoyed by this need to rename class parameters to avoid creating fields.

See this symptom and the shadowing issue.

In this case, it results in an interop problem.

scala> :pa
// Entering paste mode (ctrl-D to finish)

class Vector2(x0: Float, y0: Float) extends gdx.math.Vector2(x0, y0) {
  def copy(x: Float = this.x, y: Float = this.y): Vector2 = {
    new Vector2(x, y)
  }
}

object Vector2 {
  def apply(x: Float = 0, y: Float = 0): Vector2 = {
    new Vector2(x, y)
  }
}

// Exiting paste mode, now interpreting.

defined class Vector2
defined object Vector2

scala> Vector2(1, 2).x
res1: Float = 1.0

Footnote:

Before,

scala> Vector2(1, 2).x
java.lang.IllegalAccessError: tried to access field Vector2.x from class 
  ... 40 elided

because this.x creates a private field in the subclass.

The erroneous expression shouldn't be trying to access that field; in the absence of an accessor x, it should

scala> (Vector2(1,2): gdx.math.Vector2).x
res2: Float = 1.0

You can't circumvent the shadowing with super.x, because it's not a def.

0
votes

I tried many things and it does look like the conclusion you came to in your edit is correct.

There is a more circuitous way to make this work, however:

package rrs.scribble

import  com.badlogic.gdx.math

class Vector2(x: Float, y: Float) {
  val gdxV2 = new math.Vector2(x, y)
}

object Vector2 {
  implicit class V2(v2: Vector2)
  extends math.Vector2(v2.gdxV2)
}

object  Demo {
  import Vector2._

  def main(args: Array[String]) {
    val v2a = new Vector2(12.34f, 56.78f)
    val v2b = new Vector2(12.345f, 56.789f)

    println(s"v2=$v2a; v2=$v2a; sum=${v2a.add(v2b)}")
    println(s"(v2a =E(0.01) v2b)=${v2a.epsilonEquals(v2b, 0.01f)}")
    println(s"(v2a =E(0.001) v2b)=${v2a.epsilonEquals(v2b, 0.001f)}")

    // Note: This won't compile, apparently because
    // hasOppositeDirection is inherited not implemented within Vector2

// println(s"v2=$v2a; sum=$sumA; hasOppositeDirection=${v2.hasOppositeDirection}") } }

Running this yields:

scala> rrs.scribble.Demo.main(Array[String]())
v2=rrs.scribble.Vector2@6e8d0165; v2=rrs.scribble.Vector2@6e8d0165; sum=[24.685001:113.569]
(v2a =E(0.01) v2b)=true
(v2a =E(0.001) v2b)=false

Unfortunately, as the comment notes, this seems to fail for methods inherited by Vector2. I'm not sure why that is, actually. However this approach has an additional advantage. You could, e.g., define +, - etc. on V2 to enhance the usability of the underlying gdx.math.Vector2.