1. Class Hierarchies
熟悉PostgreSQL的朋友一定深知它的inherit表. 在Ruby中也有类似的结构, 例如前一篇BLOG最后的代码部分有举例.
如下 :

OtherClass 继承自Object
Object继承自BasicObject
Class继承自Module
Module继承自Object
我们可以从以下代码验证 :
执行结果 :
class Myclass end class Mysubclass < Myclass end puts(Mysubclass.superclass) # superclass方法用于查找超类. 查找Mysubclass的超类 puts(Myclass.superclass) # superclass方法用于查找超类. 查找Myclass的超类 puts(Class.superclass) # 查找Class的超类 puts(Module.superclass) puts(Object.class) p(BasicObject.superclass) # BasicObject没有超类, 可以认为是ROOT Class, 1.9的Object和BasicObject是从1.8的Object拆分而来. 1.8中没有BasicObject.
AI 代码解读
执行结果 :
Myclass Object Module Object Class nil
AI 代码解读
subclass继承了superclass的attribute和method. 并且subclass可以复写superclass的方法. 新增superclass中不存在的方法和attribute等.
从一个书本上的例子可以得到验证 :
执行结果 :
2. Superclasses and Subclasses
class Thing # 本例中Thing是Treasure的superclass def initialize( aName, aDescription ) @name = aName @description = aDescription end def get_name return @name end def set_name( aName ) @name = aName end def get_description return @description end def set_description( aDescription ) @description = aDescription end end class Treasure < Thing # 定义Treasure类继承自Thing def initialize( aName, aDescription, aValue ) # 复写superclass的initialize super( aName, aDescription ) # 调用superclass的同名方法(initialize).super表示传递所有参数调用superclass的同名方法, super()表示不传递任何参数调用superclass的同名方法, super(aName, aDescription) 表示传递两个参数调用superclass的同名方法. @value = aValue # 复写的initialize方法中新增了一个实例变量@value. end def get_value # 比superclass新增的方法. return @value end def set_value( aValue ) # 比superclass新增的方法. @value = aValue end end thing = Thing.new('digoal','a lucky dba') treasure = Treasure.new('digoal','a lucky dba','1983') puts(thing.inspect) puts(treasure.inspect) treasure.set_description('DBA') # 调用superclass中的method puts(treasure.inspect) # 查看@description是否更改.
AI 代码解读
执行结果 :
#<Thing:0x1b46f68 @name="digoal", @description="a lucky dba"> #<Treasure:0x1b46e30 @name="digoal", @description="a lucky dba", @value="1983"> #<Treasure:0x1426a40 @name="digoal", @description="DBA", @value="1983">
AI 代码解读
2. Superclasses and Subclasses
在第一点中已经讲得比较详细, 下面再举一个super的例子.
执行结果 :
class A def to_s puts("it's class A") end def amethod puts("it's class A's amethod") end end class B < A def to_s super() # super在这里将执行A中的to_s方法. puts("it's class B") end end class C < B def to_s puts("it's class C") end def amethod super() # super在这里将执行A中的amethod方法. 因为C继承自B, B继承自A. 虽然B中没有定义amethod方法, 但是从A中继承了. 所以B的实例可以调用amethod, C的实例也可以调用amethod. end end b = B.new b.to_s b.amethod puts("next..") c = C.new c.to_s c.amethod
AI 代码解读
执行结果 :
it's class A it's class B it's class A's amethod next.. it's class C it's class A's amethod
AI 代码解读
3. Passing Attribute value to Superclass
这个在前面的例子也讲到了, 传递attribute给superclass.
实际上就是在subclass的initialize方法中调用super方法.
例如 :
# This passes a, b, c to the superclass def initialize( a, b, c, d, e, f ) super( a, b, c ) end # This passes a, b, c to the superclass def initialize( a, b, c ) super end # This passes no arguments to the superclass def initialize( a, b, c) super() end
AI 代码解读
4. Accessor Methods
前面多个例子中都提到了对实例的变量进行初始化, 获取实例变量以及修改实例变量的值.
但是由于实例变量只允许在实例内访问, 不能直接从外面访问. 所以每次获取和修改实例变量都需要写对应的类方法来实现.
下面我们来看个例子, 如何更加简洁的来获取和修改实例变量的值.
执行结果 :
class A def initialize(a,b) @x = a @y = b end def x # 定义一个与实例变量x同名的方法 return @x end def x=(a) # 定义一个包含实例变量,x= 的方法. 注意x=之间没有空格. @x = a end def y return @y end def y=(a) @y = a end end a = A.new('digoal','zhou') p a a.x = 'DIGOAL' # 使用a.x = 对@x进行修改或赋值. a.y = 'ZHOU' puts(a.x) # 使用a.x 获取@x的返回值. puts(a.y)
AI 代码解读
执行结果 :
#<A:0x757ab8 @x="digoal", @y="zhou"> DIGOAL ZHOU
AI 代码解读
5. Attribute Readers and Writers
我们注意到上面的写法还是太繁琐, 下面来看一个更简单的例子 :
执行结果 :
注意attr_writer和attr_reader方法会自动创建对应名字的实例变量, 即使在initialize方法中没有传入.如下 :
执行结果 :
另外, 使用非标准的attr_reader举例, 例如需要在获取实例变量时需要转换成大写 :
执行结果 :
class A def initialize(a,b) @x = a @y = b end attr_reader( :x, :y ) # attr_reader方法, 参数是Symbol, 与实例变量同名的Symbol. 相当于前面例子中定义的x,y方法. attr_writer( :x, :y ) # attr_writer方法, 参数是Symbol, 与实例变量同名的Symbol. 相当于前面例子中定义的x=,y=方法. 如果这里是attr_writer( :x ), 那么就没有定义y=方法. end a = A.new('digoal','zhou') p a a.x = 'DIGOAL' a.y = 'ZHOU' puts(a.x) puts(a.y)
AI 代码解读
执行结果 :
#<A:0x1ee0340 @x="digoal", @y="zhou"> DIGOAL ZHOU
AI 代码解读
注意attr_writer和attr_reader方法会自动创建对应名字的实例变量, 即使在initialize方法中没有传入.如下 :
class A def initialize(a,b) @x = a @y = b end attr_reader( :x, :y, :z ) attr_writer( :x, :y, :z ) # 调用 z= 方法后将创建@z变量. end a = A.new('digoal','zhou') p a a.x = 'DIGOAL' a.y = 'ZHOU' puts(a.x) puts(a.y) a.z = 'it\'s Z' puts(a.z) p a
AI 代码解读
执行结果 :
#<A:0x1ab7fb8 @x="digoal", @y="zhou"> DIGOAL ZHOU it's Z #<A:0x1ab7fb8 @x="DIGOAL", @y="ZHOU", @z="it's Z">
AI 代码解读
另外, 使用非标准的attr_reader举例, 例如需要在获取实例变量时需要转换成大写 :
class A def initialize(a,b) @x = a @y = b end attr_reader( :x ) # 这里未传入 :y Symbol. attr_writer( :x, :y ) def y # y方法用于输出首字母大写的@y return @y.capitalize end end a = A.new('digoal','zhou') p a puts(a.y)
AI 代码解读
执行结果 :
#<A:0x1da0348 @x="digoal", @y="zhou"> Zhou
AI 代码解读
但是使用attr_reader和attr_writer还是闲麻烦怎么办.
还可以有更方便的吗?
如下 :
执行结果 :
class A def initialize(a,b) @x = a @y = b end attr_accessor( :x, :y ) end a = A.new('digoal','zhou') p a puts(a.x) puts(a.y)
AI 代码解读
执行结果 :
#<A:0x1e50508 @x="digoal", @y="zhou"> digoal zhou
AI 代码解读
attr_accessor 相当于同时执行了attr_reader和attr_writer
6. Calling Methods of a Superclass
这个在前面已经列举过了, 在subclass的方法中, 使用super方法调用superclass的同名方法.
7. Class Variables
前面我们接触到了localvar, globalvar, instance variable.
接下来引入class variable, 以
@@开头.
例如我们有一个Student类, 每新增一个同学班上的同学总数就加1 :
执行结果 :
查看Student的类方法 :
8. The Root of All Classes
class Student @@count = 0 # 定义一个class variable, 初始值=0 def initialize(a,b) @@count += 1 # 每次调用initialize方法时, @@count自增1. @firstname = a @lastname = b end def self.count # 定义一个属于Student这个类的方法. return @@count end def count # 定义一个instance 方法. 这两个方法虽然都叫count, 但是一个只能用Student调用, 一个只能在新建了Student实例后通过实例调用, 以后会将到这个. 我们可以定义属于类的方法, 还可以定义属于对象的方法. 通过Student.singleton_methods可以查看它的类方法. return @@count end end puts( Student.count ) a = Student.new('Zhou','Digoal') puts( Student.count ) # 通过Student类调用count方法 puts( a.count ) # 通过a对象调用count方法. b = Student.new('Zhou','Digoal') puts( Student.count ) puts( a.count ) puts( b.count ) c = Student.new('Zhou','Digoal') puts( Student.count ) puts( a.count ) puts( b.count ) puts( c.count )
AI 代码解读
执行结果 :
0 1 1 2 2 2 3 3 3 3
AI 代码解读
查看Student的类方法 :
puts( Student.singleton_methods ) 返回 count
AI 代码解读
8. The Root of All Classes
最开始已经讲过了.
1.9 中所有类的根类都是BasicObject
1.8 则为Object.
9. Nested Class
内嵌class, 这个用法比较特殊, 但是可以说明一个问题, 通过 :: 可以调用内嵌类或常量.
举例 :
执行结果 :
class A class NestedA # 这里定义了一个A的内嵌类NestedA def initialize(a) # 这里是内嵌类的内容 @x = a end attr_accessor( :x ) end def initialize(b) # 这里是A类的内容 @y = b end attr_accessor( :y ) end a = A::NestedA.new('digoal') # 定义一个内嵌类的对象 p a b = A.new('DIGOAL') # 非内嵌类的对象 p b
AI 代码解读
执行结果 :
#<A::NestedA:0x1e40c98 @x="digoal"> #<A:0x1e40bc0 @y="DIGOAL">
AI 代码解读
10. Extend Class or Partial Class
注意, Ruby的Class的定义非常灵活, 所以扩展性非常强, Class的内容可以不需要写在一个
class A
end
块内.
因此 :
等同于 :
正因为这样, RUBY的扩展性很强, 例如系统自带的Array类, 我想给它加个方法, 如下 :
输出 :
class A def initialize( a, b ) @x = a @y = b end end # 这中间可以写其他代码, 如定义class B end. 没有关系. class A attr_accessor( :x, :y ) end
AI 代码解读
等同于 :
class A def initialize( a, b ) @x = a @y = b end attr_accessor( :x, :y ) end
AI 代码解读
正因为这样, RUBY的扩展性很强, 例如系统自带的Array类, 我想给它加个方法, 如下 :
class Array def mymethod # 给Array类新增一个mymethod方法. puts('my Array\' mymethod') end end [1,2,3].mymethod
AI 代码解读
输出 :
my Array' mymethod
AI 代码解读
11. Constants Inside Classes
常量以大写开头, 从外部访问也是使用 :: .
输出 :
【参考】
class A Count = 1 end puts( A::Count )
AI 代码解读
输出 :
1
AI 代码解读
【参考】
The Book Of Ruby
Ruby 1.9.3 API