Wie gebe ich einen Typen für Float und Double auf einem generischen Typ in Scala gebunden?
-
27-09-2019 - |
Frage
Ich schreibe ein paar einfache Vektor und Matrix-Klassen. Sie sehen aus wie folgt aus:
// Vector with Floats
case class Vector3f(x: Float, y: Float, z: Float) {
def +(v: Vector3f) = Vector3f(x + v.x, y + v.y, z + v.z)
}
// Vector with Doubles
case class Vector3d(x: Double, y: Double, z: Double) {
def +(v: Vector3d) = Vector3d(x + v.x, y + v.y, z + v.z)
}
Wenn ich mit weiteren Methoden weitergehen und Klassen wie Point3f / d, Vector4f / d, Matrix3f / d, Matrix4f / d ... das wird eine Menge Arbeit sein. Uff ... Also dachte ich Generika konnte mögliche Hilfe hier und entfernen Redundanz von meiner Code-Basis. Ich dachte an so etwas wie folgt aus:
// first I define a generic Vector class
case class Vector3[@specialized(Float, Double) T](x: T, y: T, z: T) {
def +(v: Vector3[T]) = new Vector3[T](x + v.x, y + v.y, z + v.z)
}
// than I use some type aliases to hide the generic nature
type Vector3f = Vector3[Float]
type Vector3d = Vector3[Double]
Die Idee ist, dass die scala Compiler Klassen für Vector3 [Float] und Vector3 [Double] ähnlich wie ein C ++ Vorlage tun würde, spezialisiert erzeugt. Leider muss ich auf dem Typparameter [T] der Klasse Vector3, so dass der Betreiber verpflichtet, irgendeine Art setzen + auf T. Meine Frage definiert ist: Wie kann ich Vector3 [Float] schreiben, dass es die gleichen Leistungsmerkmale wie Vector3f hat? Kontext:. Ich mag die Vector3f / Vector3D Klassen in Kollisionserkennungscode verwenden ... so die Leistung für mich nicht egal
Lösung
Verwenden Sie einen Kontext gebunden von Fractional:
case class Vector3[@specialized(Float, Double) T : Fractional](x: T, y: T, z: T) { ...
dann innerhalb des Körpers der Klasse eine Instanz der arithmetischen Operatoren erhalten:
val fractOps = implicitly[Fractional[T]]
schließlich seine Mitglieder in den Gültigkeitsbereich der Klasse importieren:
import fractOps._
Danach können Sie gewöhnliche Infix Operationen auf Werten vom Typ T schreiben innerhalb der Klasse verwendet. Leider müssen Sie fractOps.div(a, b)
statt a / b
für die Division verwendet werden.