RSpec中定义的匿名类无法响应new方法的问题咨询
你遇到的问题核心在于你继承的是Ruby的Integer类,而它属于Ruby里特殊的不可变值类型,和普通Ruby类的实例创建逻辑完全不同。
为什么普通类只需要initialize就能实例化?
咱们平时看的教程里,大多是继承普通类(比如Object或者自定义类),这些类的new方法是Class类提供的默认实现——它会先调用allocate分配内存创建空实例,然后自动调用实例的initialize方法完成初始化。所以只需要定义initialize,就能通过new正常创建实例。
为什么继承Integer时initialize不生效?
Ruby的数值类(Integer、Float、String这些不可变值类型)的new方法是私有方法,而且Ruby本身并不推荐用new来创建它们的实例——你平时创建Integer实例都是用4、Integer(4)这种方式,而不是Integer.new(4)(不信你试试直接在irb里调用Integer.new(4),同样会报错private method 'new' called for Integer:Class)。
当你继承Integer后,你的匿名类也继承了这个私有new方法,所以你调用fake_class.new(4)时,本质是在调用一个私有方法,自然会报错。
为什么自己定义new能生效?
你显式在类的单例类里定义new方法,其实是覆盖了继承来的私有new方法,把它改成了公有的实例创建方法,所以能正常调用,但这确实不符合普通Ruby类的行为——因为普通类的new不需要自己定义,是由Class默认提供的。
解决方案:避开继承数值类,自己实现需要的功能
如果你的需求是创建一个类似Integer的自定义类,更合理的做法是不继承Integer,而是继承Object,然后实现你需要的方法:
let!(:fake_class) do Class.new(Object) do def initialize(value) @value = value.to_i # 确保是整数类型 end def ==(other) @value == other end def coerce(other) [@value, other] end def to_s @value.to_s end # 如果需要支持数值运算,可以按需定义对应的方法 def +(other) @value + other end def -(other) @value - other end end end
这样调用fake_class.new(4)就会像教程里的普通类一样,自动调用initialize完成实例化,完全符合预期行为。
内容的提问来源于stack exchange,提问作者Thermatix

