我的问题是:如何在内置类(例如Integer.new。+)上重载运算符,但仅限于某些情况,具体取决于第二个操作数的类
这是我正在寻找的行为:
myObject = myClass.new
1 + myObject #=> special behaviour
1 + 2 #=> default behaviour (3)
例如,在Python中,我将在myClass上定义一个__radd__
方法来覆盖大小写1。
我已尝试使用super
,但显然Numeric
没有运算符方法。
理想情况下,我正在寻找的是一种提取+
方法并重命名的方法。
像这样:
class Integer
self.plus = self.+ # you know what i mean, I don't know how else to express this.
# I also know that methods don't work like this, this is just to
# illustrate a point.
def + other
other.class == myClass ? special behaviour : self.plus other
end
end
感谢您的帮助
答案 0 :(得分:3)
到目前为止,这里发布的两种方法都是传统的Rails方式,明显错误。它依赖于这样一个事实:该类没有名为plus
的方法,而 nobody将重新打开该类以创建一个名为plus
的方法。否则事情就会发疯。
正确的解决方案是Module#prepend
:
Integer.prepend(Module.new do
def + other
case other
when Fixnum then special_behaviour
else super(other)
end
end
end)
答案 1 :(得分:2)
是的,你可以覆盖标准库中几乎任何东西的行为来实现结果,但这会损害对代码的理解,并在将来的某个时候回来咬你。
在这种特殊情况下,Fixnum#+
旨在获取数值并返回数值结果。如果我们想要定义自己的类来与Fixnum#+
进行交互,我们需要了解设计合同并遵守它。
Ruby中的一般约定是使用duck typing。我们不关心对象的类,我们只关心它是否像/可以转换为我们想要的对象。例如:
class StringifiedNumber
def initialize(number)
@number = number
end
# String#+ calls to_str on any object passed to it
def to_str
# replace with number to string parsing logic
"one hundred"
end
end
> "total: " + StringifiedNumber.new(100)
=> "total: one hundred"
因为你可以混合使用整数,浮点数,复数等等,所以事情有点复杂。处理这个的约定是定义一个coerce
方法,它返回两个相同类型的元素然后用于执行请求的操作。
class NumberfiedString
def initialize(string)
@string = string
end
def to_i
# replace with complicated natural language parsing logic
100
end
def +(other_numberfied_string)
NumberfiedString.new(self.to_i + other_numberfied_string.to_i)
end
# For types which are not directly supported,
# Fixnum#+(target) will call the equivalent of
# target.coerce[0] + target.coerce[1]
def coerce(other)
[NumberfiedString.new(other.to_s), self]
end
end
> NumberfiedString.new("one hundred") + NumberfiedString.new("one hundred")
=> #<NumberfiedString:0x007fadbc036d28 @string=200>
> 100 + NumberfiedString.new("one hundred")
=> #<NumberfiedString:0x007fadbc824c88 @string="200">
回答OP的后续问题:
没有相当于Python的radd和相关方法吗? (哪里, 如果第一个操作数不支持操作或类型,则 第二个操作数接管)
class MyClass
def +(other)
puts "called +"
end
def coerce(other)
[self, other]
end
end
> 1 + MyClass.new
called +
=> nil