Ruby 类的本质
前面的章节中介绍了类的定义与实例化,本章节我们来深入探讨一下类的本质。
1. 超类
现在我们定义一两个类:Student
和Person
,令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会先找到他的类Student
(class
方法),从这个类中没有找到方法,然后继续向上找它的超类(superclass
方法),从Person
中找到了age
和name
。
这种方法查找顺序的机制,我们可以使用祖先链(ancestors)来表示。
实例:
p Student.ancestors
# ---- 输出结果 ----
[Student, Person, Object, Kernel, BasicObject]
解释:我们可以看到,祖先链是从student的类逐级向上查找的,需要注意的是,Kernel
是一个模块,因为Object
类include了Kernel
模块,所以Kernel
也会出现在祖先链里面。
在之前的学习中我们了解到,在方法名称相同的时候,include
模块不会覆盖类原本的方法,而Ruby 2.0以后的perpend
方法会覆盖掉类原本的方法。因此使用include
或prepend
引入模块会出现两种不同的祖先链。
实例:
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. 小结
本章节中的重点是:
- 所有的类都是
Object
的子类。(Ruby 1.8 - 1.9 是BasicObject
); - 所有的类都是
Class
的实例,包括它本身; - 所有对象的方法都在类中,根据祖先链来进行查找;
- 模块会根据
include
和perpend
出现在祖先链中不同的位置上。