Можно ли добавить метод к встроенному типу в Scala?
-
08-07-2019 - |
Вопрос
Я хотел бы добавить метод к встроенному типу (например,Double), так что я могу использовать infix
оператор.Это возможно?
Решение
Да и нет.Да, ты можешь это сделать казаться как будто вы добавили метод в double
.Например:
class MyRichDouble(d: Double) {
def <>(other: Double) = d != other
}
implicit def doubleToSyntax(d: Double) = new MyRichDouble(d)
Этот код добавляет ранее недоступный <>
оператор для любого объекта типа Double
.Пока doubleToSyntax
метод находится в области видимости, поэтому его можно вызывать без уточнений, будет работать следующее:
3.1415 <> 2.68 // => true
Часть ответа «нет» исходит из того факта, что вы на самом деле ничего не добавляете к Double
сорт.Вместо этого вы создаете конверсию из Double
к новому типу, который определяет нужный вам метод.Это может быть гораздо более мощный метод, чем открытые классы, предлагаемые многими динамическими языками.Кроме того, он полностью типобезопасен.:-)
Некоторые ограничения, о которых вам следует знать:
- Эта техника не позволяет вам удалять или переопределить существующие методы, просто добавьте новые
- Метод неявного преобразования (в данном случае
doubleToSyntax
) обязательно должен находиться в области видимости, чтобы желаемый метод расширения был доступен.
Идиоматически неявные преобразования либо помещаются в одноэлементные объекты, либо импортируются (например, import Predef._
) или внутри признаков и унаследовано (например. class MyStuff extends PredefTrait
).
Небольшое отступление:«Инфиксные операторы» в Scala на самом деле являются методами.Никакой магии, связанной с <>
метод, который позволяет ему быть инфиксным, синтаксический анализатор просто принимает его таким.Если хотите, вы также можете использовать «обычные методы» в качестве инфиксных операторов.Например, Stream
класс определяет take
метод, который принимает один Int
параметр и возвращает новый Stream
.Это можно использовать следующим образом:
val str: Stream[Int] = ...
val subStream = str take 5
А str take 5
выражение буквально идентично str.take(5)
.
Другие советы
Эта функция пригодилась для реализации класса, выполняющего оценку ошибок:
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
}
}