2012-06-12 3 views
3

Я использую jMonkeyEngine (Java Game Engine) в Scala, который до сих пор хорошо работает, но теперь я спрашиваю себя, есть ли элегантный способ перегрузки операторов для Vector3f (и аналогичные) классы. Моя первая идея заключалась в том, чтобы наследовать от Vector3f и перегрузить операторы, но Vector3f является окончательным, так что это не вариант. Затем я подумал, что я мог бы объект синглтон/SCALA со статическими методами, которые работают на Vector3f, как показано ниже, но это не работает, либо:Добавить операторов Scala в конечный класс Java

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 сделать это, или это просто невозможно?

ответ

12

Или может быть синтаксис Scala, который я не знаю, чтобы делать такие вещи?

Да, есть implicits:

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 } 
+2

О, ничего себе, я влюблен - это оооооооочень. Спасибо! – Ole

+0

Примечание: это должно быть размещено в объекте, а не в верхней области. Чтобы использовать неявное преобразование, вам нужно импортировать этот объект. (Это, наверное, понятно экспертам Scala, это было не для меня, поэтому я добавляю этот комментарий). – Suma

1

Примечание: Это может привести к очень нечитаемому коду.

Вы можете использовать неявные преобразования и создать какой-то класс обертки вокруг Vector3f (как самой стандартной библиотеки Scala с несколькими классами Java, таких как коллекции Java see here.

// 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) = { 
     // ... 
    } 
} 
3

В дополнение к тому, что было говорит о неявных классов выше, начиная от Scala 2.10, вы будете иметь возможность использовать value classes, чтобы избежать создания промежуточного объекта.

по сути, вы просто должны сделать VectorMath выше продлить AnyVal.Если вы не используете объект VectorMath в качестве возвращаемого значения метода и вместо этого просто вызываете операции VectorMath, ни один объект для VectorMath не будет выделен во время выполнения.

-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 

Требуется небольшой синтаксический штраф, но это еще один вариант.

+0

Проблема с этим подходом заключается в том, что Vector3f является «окончательным классом» из jMonkeyEngine. Поэтому вы не можете расширять его и добавлять свои собственные методы, как это неявно делается в вашем решении. – sschaef

+0

k, поэтому имплицитно нажимать на него является решение - просто хотелось показать, что существует альтернативный подход. –

Смежные вопросы