类实例变量
2007年1月9日
当你学习对象时,通常会学习到它们可以捕获两种类型的数据:实例和类。实例变量是最常见的情况,数据随对象的每个实例而变化。类变量,通常称为静态变量,在类的所有实例之间共享。每个实例都指向相同的值,并且所有实例都可以看到任何更改。类变量比实例变量少得多,尤其是可变类变量。
类变量与继承的交互方式是一个特殊的问题。考虑一个用于存储自身实例的类变量。(如果对 ruby 不熟悉,请参阅我的 阅读指南。)
#ruby class Employee @@instances = [] def self.instances return @@instances end def store @@instances << self end def initialize name @name = name end end Employee.new('Martin').store Employee.new('Roy').store Employee.new('Erik').store puts Employee.instances.size
这里没有意外,有三个员工。但现在试试这个。
#ruby class Employee @@instances = [] def self.instances @@instances end def store @@instances << self end def initialize name @name = name end end class Programmer < Employee; end class Overhead < Employee; end Overhead.new('Martin').store Overhead.new('Roy').store Programmer.new('Erik').store puts Overhead.instances.size puts Programmer.instances.size
这里的输出是 3 和 3,而我们可能更希望是 2 和 1。原因是类变量在类的所有实例之间共享,包括所有子类。有两个类,但只有一个变量。
有时,这种跨层次结构的变量正是我们想要的,但有时,就像在这种情况下,我们更希望每个类都有一个不同的变量。我第一次在 Smalltalk 的一些后期版本中遇到了这个概念,它被称为类实例变量。你可以像使用类变量一样引用类实例变量,但每个类都会得到不同的值。
OO 语言中对类实例变量的支持并不常见,但自己实现它并不难。最明显的方法是使用以类名为键的字典。
#ruby class Employee @@instances = {} def self.instances @@instances[self] end def store @@instances[self.class] ||= [] @@instances[self.class] << self end def initialize name @name = name end end class Overhead < Employee; end class Programmer < Employee; end Overhead.new('Martin').store Overhead.new('Roy').store Programmer.new('Erik').store puts Overhead.instances.size puts Programmer.instances.size
你可以在任何 OO 语言中使用这种技术。然而,Ruby 实际上有类实例变量。
#ruby class Employee class << self; attr_accessor :instances; end def store self.class.instances ||= [] self.class.instances << self end def initialize name @name = name end end class Overhead < Employee; end class Programmer < Employee; end Overhead.new('Martin').store Overhead.new('Roy').store Programmer.new('Erik').store puts Overhead.instances.size puts Programmer.instances.size
类实例变量的定义是片段 class << self; attr_accessor :instances; end
。出于我不想深入探讨的原因,这在类 employee 上定义了一个实例变量(以及 getter 和 setter),该变量被其后代继承。与类变量不同,这些类实例变量对于每个类对象将采用不同的值。
类实例变量非常罕见,但在你需要它们的时候很有用。