章节索引 :

Ruby 类的本质

前面的章节中介绍了类的定义与实例化,本章节我们来深入探讨一下类的本质。

1. 超类

现在我们定义一两个类:StudentPerson,令Student继承Person,然后通过调用superclass方法来输出它的超类。

实例:

class Person
end

class Student < Person
end

puts Student.superclass

# ---- 输出结果 ----
Person

由上面输出可知,超类实际上是当前类所继承的类。

现在让我们继续获取Person的超类。

实例:

puts Person.superclass

# ---- 输出结果 ----
Object

Object是一个特殊的类,所有的类都是Object的子类。(在Ruby 1.8 ~ 1.9中,每个类都是BasicObject的子类)

我们输出Object的超类,得到了BasicObject,它是Ruby类结构体系的根节点。

实例:

puts Object.superclass

# ---- 输出结果 ----
BasicObject

2. 类实际上是对象

现在让我们实例化Person,获得一个person对象。还记得class方法吗,它是获取一个对象所属的类的方法。

实例:

person = Person.new
puts person.class
puts Person.class

# ---- 输出结果 ----
Person
Class

解释:第一条输出结果代表了person对象所属于Person类,同理,第二条输出结果代表了Person类,是Class类的对象。

Tips:每一个类都是Class的实例,这也意味着Class类也是Class类自己的实例。

实例:

puts Class.class

# ---- 输出结果 ----
Class

因此综上所述,所有的类都是对象

Tips:Class的超类是Module,所以可以说所有的类其实也是模块。

实例:

puts Class.superclass
puts Module.superclass

# ---- 输出结果 ----
Module
Object

3. 祖先链

由之前的学习,我们知道了Ruby的方法都是定义在类中,调用方法前,Ruby会在对象的类中查找到那个方法,现在让我们学习一下它的运作原理。

举一个例子:

"hello".reverse	

在这里,"hello"调用了reverse方法,"hello"这个字符串对象作为调用方法的对象被称为接收者receiver)。

现在我们在Person中定义方法。

实例:

class Person
  def age
    18
  end
  
  def name
    "Andrew"
  end
  
end

class Student < Person
end

student = Student.new
puts student.name
puts student.age

# ---- 输出结果 ----
Andrew
18

解释:刚刚这个例子表示了一个简单的继承关系。Ruby在查找方法时遵循一个先右一步再向上的原则,如刚刚的student对象,Ruby会先找到他的类Studentclass方法),从这个类中没有找到方法,然后继续向上找它的超类(superclass方法),从Person中找到了agename

这种方法查找顺序的机制,我们可以使用祖先链ancestors)来表示。

实例:

p Student.ancestors

# ---- 输出结果 ----
[Student, Person, Object, Kernel, BasicObject]

解释:我们可以看到,祖先链是从student的类逐级向上查找的,需要注意的是,Kernel是一个模块,因为Object类include了Kernel模块,所以Kernel也会出现在祖先链里面。

在之前的学习中我们了解到,在方法名称相同的时候,include模块不会覆盖类原本的方法,而Ruby 2.0以后的perpend方法会覆盖掉类原本的方法。因此使用includeprepend引入模块会出现两种不同的祖先链。

实例:

module A
end

module B
end

class Person
  prepend A
  include B
end

class Student < Person
end

p Student.ancestors

# ---- 输出结果 ----
[Student, A, Person, B, Object, Kernel, BasicObject]

解释:从比较容易理解的层面上,因为模块A的优先级比Person更高,模块B的优先级比Person低,所以当查到Student之后,先会去找模块A,然后是Person,然后是模块B …

4. 小结

本章节中的重点是:

  1. 所有的类都是Object的子类。(Ruby 1.8 - 1.9 是BasicObject);
  2. 所有的类都是Class的实例,包括它本身;
  3. 所有对象的方法都在类中,根据祖先链来进行查找;
  4. 模块会根据includeperpend出现在祖先链中不同的位置上。