Ist es möglich, ein Verfahren zu einem eingebauten Typ in Scala hinzufügen?
-
08-07-2019 - |
Frage
Ich mag eine Methode zu einem eingebauten Typ (z Doppel) hinzuzufügen, so dass ich einen infix
Operator verwenden kann. Ist das möglich?
Lösung
Ja und nein. Ja, können Sie es scheinen wie Sie eine Methode, um double
hinzugefügt haben. Zum Beispiel:
class MyRichDouble(d: Double) {
def <>(other: Double) = d != other
}
implicit def doubleToSyntax(d: Double) = new MyRichDouble(d)
Dieser Code fügt die zuvor nicht verfügbar <>
Betreiber auf ein beliebiges Objekt vom Typ Double
. Solange die doubleToSyntax
Methode in ihrem Umfang ist, so dass sie ohne Qualifikation geltend gemacht werden könnten, werden folgende Arbeiten:
3.1415 <> 2.68 // => true
Der „Nein“ ein Teil der Antwort kommt von der Tatsache, dass Sie nicht wirklich etwas auf die Double
Klasse hinzufügen. Stattdessen werden Sie eine Konvertierung von Double
auf eine neue Art zu schaffen, die die Methode, die Sie wollen nicht definiert. Dies kann durch viele dynamischen Sprachen angeboten eine viel leistungsfähige Technik als die Open-Klassen sein. Es kommt auch vollständig typsicher zu sein. : -)
Einige Einschränkungen sollten Sie sich bewusst sein von:
- Diese Technik ermöglicht es Ihnen nicht zu entfernen oder neu zu definieren bestehende Methoden, fügen Sie einfach neue
- Die implizite Umwandlungsmethode (in diesem Fall
doubleToSyntax
) absolut in-Bereich für die gewünschten Nebenverfahren zur Verfügung stehen muss
idiomatically werden implizite Konvertierungen entweder innerhalb Singleton Objekte platziert und importiert (z.B. import Predef._
) oder innerhalb von Eigenschaften und vererbt (z.B. class MyStuff extends PredefTrait
).
Leichte beiseite: „Infixoperatoren“ in Scala sind eigentlich Methoden. Es gibt keine magische mit dem <>
Verfahren verbunden, die es Infix sein kann, einfach der Parser akzeptiert es auf diese Weise. Sie können auch „normale Verfahren“ als Infixoperatoren verwenden, wenn Sie möchten. Zum Beispiel definiert die Klasse ein Stream
take
Verfahren, die einen einzelnen Int
Parameter annimmt und gibt eine neue Stream
. Dies kann auf folgende Weise verwendet werden:
val str: Stream[Int] = ...
val subStream = str take 5
Der str take 5
Ausdruck ist buchstäblich identisch mit str.take(5)
.
Andere Tipps
Mit dieser Funktion kam praktisch eine Klasse leistungsFehlerAbschätzung zu implementieren:
object errorEstimation {
class Estimate(val x: Double, val e: Double) {
def + (that: Estimate) =
new Estimate(this.x + that.x, this.e + that.e)
def - (that: Estimate) =
new Estimate(this.x - that.x, this.e + that.e)
def * (that: Estimate) =
new Estimate(this.x * that.x,
this.x.abs*that.e+that.x.abs*this.e+this.e*that.e)
def / (that: Estimate) =
new Estimate(this.x/that.x,
(this.x.abs*that.e+that.x.abs*this.e)/(that.x.abs*(that.x.abs-that.e)))
def +- (e2: Double) =
new Estimate(x,e+e2)
override def toString =
x + " +- " + e
}
implicit def double2estimate(x: Double): Estimate = new Estimate(x,0)
implicit def int2estimate(x: Int): Estimate = new Estimate(x,0)
def main(args: Array[String]) = {
println(((x: Estimate) => x+2*x+3*x*x)(1 +- 0.1))
// 6.0 +- 0.93
println(((x: Estimate) => (((y: Estimate) => y*y + 2)(x+x)))(1 +- 0.1))
// 6.0 +- 0.84
def poly(x: Estimate) = x+2*x+3/(x*x)
println(poly(3.0 +- 0.1))
// 9.33333 +- 0.3242352
println(poly(30271.3 +- 0.0001))
// 90813.9 +- 0.0003
println(((x: Estimate) => poly(x*x))(3 +- 1.0))
// 27.037 +- 20.931
}
}