I would like to add a method to a built-in type (e.g. Double), so that I can use an infix
operator. Is that possible?
我想为内置类型(例如Double)添加一个方法,以便我可以使用中缀运算符。那可能吗?
2 个解决方案
#1
18
Yes and no. Yes, you can make it seem like you have added a method to double
. For example:
是的,不是。是的,你可以让它看起来像你添加了一个双倍的方法。例如:
class MyRichDouble(d: Double) {
def <>(other: Double) = d != other
}
implicit def doubleToSyntax(d: Double) = new MyRichDouble(d)
This code adds the previously-unavailable <>
operator to any object of type Double
. So long as the doubleToSyntax
method is in scope so that it could be invoked without qualification, the following will work:
此代码将以前不可用的<>运算符添加到Double类型的任何对象。只要doubleToSyntax方法在范围内,以便可以无限制地调用它,以下将起作用:
3.1415 <> 2.68 // => true
The "no" part of the answer comes from the fact that you aren't really adding anything to the Double
class. Instead, you are creating a conversion from Double
to a new type which does define the method you want. This can be a much more powerful technique than the open-classes offered by many dynamic languages. It also happens to be completely type-safe. :-)
答案的“否”部分来自于你并没有真正向Double类添加任何内容。相反,您正在创建从Double到新类型的转换,它确定了您想要的方法。这可能是一种比许多动态语言提供的开放类更强大的技术。它也恰好是完全类型安全的。 :-)
Some limitations you should be aware of:
您应该注意的一些限制:
- This technique does not allow you to remove or redefine existing methods, just add new ones
- The implicit conversion method (in this case,
doubleToSyntax
) absolutely must be in-scope for the desired extension method to be available
此技术不允许您删除或重新定义现有方法,只需添加新方法即可
隐式转换方法(在本例中为doubleToSyntax)绝对必须在范围内,以使所需的扩展方法可用
Idiomatically, implicit conversions are either placed within singleton objects and imported (e.g. import Predef._
) or within traits and inherited (e.g. class MyStuff extends PredefTrait
).
在惯用语中,隐式转换要么放在单个对象中并导入(例如导入Predef._),要么在traits中继承并继承(例如,类MyStuff扩展PredefTrait)。
Slight aside: "infix operators" in Scala are actually methods. There is no magic associated with the <>
method which allows it to be infix, the parser simply accepts it that way. You can also use "regular methods" as infix operators if you like. For example, the Stream
class defines a take
method which takes a single Int
parameter and returns a new Stream
. This can be used in the following way:
稍微说一下:Scala中的“中缀运算符”实际上就是方法。没有与<>方法相关联的魔法,它允许它作为中缀,解析器只是接受它。如果您愿意,也可以使用“常规方法”作为中缀运算符。例如,Stream类定义了一个take方法,该方法接受一个Int参数并返回一个新的Stream。这可以通过以下方式使用:
val str: Stream[Int] = ...
val subStream = str take 5
The str take 5
expression is literally identical to str.take(5)
.
str take 5表达与str.take(5)字面上相同。
#2
1
This feature came in handy to implement a class performing error estimation:
此功能在实现执行错误估计的类时非常方便:
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
}
}
#1
18
Yes and no. Yes, you can make it seem like you have added a method to double
. For example:
是的,不是。是的,你可以让它看起来像你添加了一个双倍的方法。例如:
class MyRichDouble(d: Double) {
def <>(other: Double) = d != other
}
implicit def doubleToSyntax(d: Double) = new MyRichDouble(d)
This code adds the previously-unavailable <>
operator to any object of type Double
. So long as the doubleToSyntax
method is in scope so that it could be invoked without qualification, the following will work:
此代码将以前不可用的<>运算符添加到Double类型的任何对象。只要doubleToSyntax方法在范围内,以便可以无限制地调用它,以下将起作用:
3.1415 <> 2.68 // => true
The "no" part of the answer comes from the fact that you aren't really adding anything to the Double
class. Instead, you are creating a conversion from Double
to a new type which does define the method you want. This can be a much more powerful technique than the open-classes offered by many dynamic languages. It also happens to be completely type-safe. :-)
答案的“否”部分来自于你并没有真正向Double类添加任何内容。相反,您正在创建从Double到新类型的转换,它确定了您想要的方法。这可能是一种比许多动态语言提供的开放类更强大的技术。它也恰好是完全类型安全的。 :-)
Some limitations you should be aware of:
您应该注意的一些限制:
- This technique does not allow you to remove or redefine existing methods, just add new ones
- The implicit conversion method (in this case,
doubleToSyntax
) absolutely must be in-scope for the desired extension method to be available
此技术不允许您删除或重新定义现有方法,只需添加新方法即可
隐式转换方法(在本例中为doubleToSyntax)绝对必须在范围内,以使所需的扩展方法可用
Idiomatically, implicit conversions are either placed within singleton objects and imported (e.g. import Predef._
) or within traits and inherited (e.g. class MyStuff extends PredefTrait
).
在惯用语中,隐式转换要么放在单个对象中并导入(例如导入Predef._),要么在traits中继承并继承(例如,类MyStuff扩展PredefTrait)。
Slight aside: "infix operators" in Scala are actually methods. There is no magic associated with the <>
method which allows it to be infix, the parser simply accepts it that way. You can also use "regular methods" as infix operators if you like. For example, the Stream
class defines a take
method which takes a single Int
parameter and returns a new Stream
. This can be used in the following way:
稍微说一下:Scala中的“中缀运算符”实际上就是方法。没有与<>方法相关联的魔法,它允许它作为中缀,解析器只是接受它。如果您愿意,也可以使用“常规方法”作为中缀运算符。例如,Stream类定义了一个take方法,该方法接受一个Int参数并返回一个新的Stream。这可以通过以下方式使用:
val str: Stream[Int] = ...
val subStream = str take 5
The str take 5
expression is literally identical to str.take(5)
.
str take 5表达与str.take(5)字面上相同。
#2
1
This feature came in handy to implement a class performing error estimation:
此功能在实现执行错误估计的类时非常方便:
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
}
}