Ruby中如何统计包括子类在内的所有实例创建数量?
碰到过类似的坑!核心问题出在你用的类实例变量(@count)上——它是绑定在单个类对象上的:Point类自己有一个@count,但ColoredPoint作为子类,它的类实例变量@count根本没被初始化。所以当你创建ColoredPoint实例时,self.class.count访问的是ColoredPoint的count值,此时它还是nil,自然就触发了“undefined method '+' for nil”的错误。
下面给你几种不用类变量(或者可选类变量)的解决方案,按需选择:
方案一:共享父类计数器(推荐,无类变量)
如果你想要的是一个全局计数器,统计所有Point及其子类的实例总数,可以修改父类的类方法,让子类直接复用父类的@count:
class Point @count = 0 class << self # 子类调用count时,直接返回父类的count值 def count superclass.respond_to?(:count) ? superclass.count : @count end # 子类的赋值操作,直接修改父类的count def count=(value) if superclass.respond_to?(:count=) superclass.count = value else @count = value end end end def initialize(position) @position = position self.class.count += 1 end end class ColoredPoint < Point def initialize(position, color) super(position) @color = color end end
这样不管是创建Point.new还是ColoredPoint.new,都会更新Point类的@count,而且ColoredPoint.count也会返回和Point.count一样的总数,完美解决报错问题,还避开了类变量的潜在问题。
方案二:用inherited钩子初始化子类计数器
如果希望子类也能维护自己的计数器,但父类的计数器包含所有子类实例,或者只是想解决初始化问题,可以用Ruby的inherited钩子,在子类被定义时自动同步父类的count初始值:
class Point @count = 0 class << self attr_accessor :count # 当有子类继承时,初始化子类的count为父类当前的count def inherited(subclass) subclass.count = self.count end end def initialize(position) @position = position self.class.count += 1 end end class ColoredPoint < Point def initialize(position, color) super(position) @color = color end end
这种方式下,ColoredPoint会有自己的@count,初始值和Point一致,之后创建子类实例时会递增自己的count。如果需要父类统计总数,还需要额外在子类初始化时也更新父类的count,比如在ColoredPoint的initialize里加Point.count +=1。
可选方案:用类变量(简单但需谨慎)
如果你觉得上面的方法有点复杂,在这个简单的继承场景下,用类变量(@@count)其实也不会有太大问题,改起来最直接:
class Point @@count = 0 class << self def count @@count end def count=(value) @@count = value end end def initialize(position) @position = position self.class.count += 1 end end class ColoredPoint < Point def initialize(position, color) super(position) @color = color end end
不过要注意,类变量是所有子类共享的,如果后续有更复杂的继承结构,可能会出现意外的共享问题,所以一般不推荐,但这个场景下是可行的。
备注:内容来源于stack exchange,提问作者user200783

