3

我在 Scala 中使用 jMonkeyEngine(Java 游戏引擎),到目前为止效果很好,但现在我问自己是否有一种优雅的方法可以为Vector3f(和类似的)类重载运算符。我的第一个想法是继承并重Vector3f载运算符,但Vector3f它是最终的,所以这不是一个选项。接下来我想也许我可以有一个带有静态方法的单例/scala对象Vector3f,如下所示,但这也不起作用:

object VectorMath {
    def *(factor: Float, vector: Vector3f) = vector.mult(factor)
}

//and then somewhere
import VectorMath._
var v = new Vector3f(1,2,3);
var u = 1.2f * v; //this does not work, because Float does not have * overloaded for Vector3f
var u = VectorMath.*(1.2f, v); //this does work, but defeats the purpose

所以我现在能想到的就是将 Vector3f 包装在一个新的 Scala 类中,并将操作符调用委托给适当的 Java 方法。然而,这有三个缺点:

  1. 我将不得不做很多来回转换(一个toVector3f和一个fromVector3f方法或类似的东西)。
  2. 当我有 Vector3f/ScalaVectors 数组时,这个问题会变得更糟。由于类不相关(组合除外),因此每次在 jME 中调用方法时,我都必须手动转换数组的每个元素。
  3. 即便如此,我也不知道如何为我的新 ScalaVector 类重载运算符,以使我可以将因子放在前面,即1.2f * v.

我的问题:有人能想办法让这更自然/优雅吗?你如何解决类似的问题?或者是否有我不知道的 Scala 语法来做这些事情?在 C++ 中,我会创建一个全局运算符,它将 float 和 Vector3f 作为参数并可能与它为朋友。Scala 的方法是什么,或者这根本不可能?

4

4 回答 4

12

或者是否有我不知道的 Scala 语法来做这些事情?

是的,有隐含的:

class VectorMath(f: Float) { def * (v: Vector3f) = v mult f }
implicit def VectorMath(f: Float) = new VectorMath(f)

val v = new Vector3f(1,2,3)
1.2F * v
// treated as: VectorMath(1.2F).*(v)

从 Scala 2.10 开始,隐式转换也可以写成:

implicit class VectorMath(f: Float) { def * (v: Vector3f) = v mult f }

从 2.10 开始,还有一些值类被编译器优化以获得更好的性能:

implicit class VectorMath(val f: Float) extends AnyVal { def * (v: Vector3f) = v mult f }
于 2012-06-12T20:44:30.027 回答
3

除了上面提到的隐式类之外,从 Scala 2.10 开始,您将能够使用值类来避免创建中间对象。

本质上,您只需要进行VectorMath上述扩展即可AnyVal。如果您不使用VectorMath对象作为方法的返回值,而只是调用VectorMath操作,则不会VectorMath在运行时分配对象 for。

于 2012-06-12T21:01:17.427 回答
1

注意:这可能会导致代码非常不可读。

您可以使用隐式转换并创建某种包装类Vector3f(例如 Scala 标准库本身具有多个 Java 类,例如此处的 Java 集合。

// Somewhere, for example in a package object
implicit def wrapVector3f (v: Vector3f) = new Vector3fWrapper ( v );

class Vector3fWrapper (v: Vector3f) {
    // Use the Vector3f object internally here
    // You can now delegate all methods to the underlying Vector3f object
    // For example:
    def * (v: Vector3f) = {
        // ...
    }

    // or:
    def * (f: Float) = {
        // ...
    }
}
于 2012-06-12T20:45:05.203 回答
-1

大家已经指出可以隐式地将操作添加到左侧的 Float 中,只是想指出您可以通过在其末尾添加一个冒号来创建一个右关联运算符。

class Vector3f(…) {
  …
  def *:(f: Float) = …
}

var v = new Vector3f(1,2,3);
var u = 1.2f *: v; //this works without any implicit magic or boxing overhead

它需要一个小的句法惩罚,但它是另一种选择。

于 2012-06-13T03:27:26.607 回答