Ruby 類的本質(zhì)
前面的章節(jié)中介紹了類的定義與實(shí)例化,本章節(jié)我們來深入探討一下類的本質(zhì)。
1. 超類
現(xiàn)在我們定義一兩個(gè)類:Student
和Person
,令Student
繼承Person
,然后通過調(diào)用superclass
方法來輸出它的超類。
實(shí)例:
class Person
end
class Student < Person
end
puts Student.superclass
# ---- 輸出結(jié)果 ----
Person
由上面輸出可知,超類實(shí)際上是當(dāng)前類所繼承的類。
現(xiàn)在讓我們繼續(xù)獲取Person
的超類。
實(shí)例:
puts Person.superclass
# ---- 輸出結(jié)果 ----
Object
Object
是一個(gè)特殊的類,所有的類都是Object
的子類。(在Ruby 1.8 ~ 1.9中,每個(gè)類都是BasicObject
的子類)
我們輸出Object
的超類,得到了BasicObject
,它是Ruby類結(jié)構(gòu)體系的根節(jié)點(diǎn)。
實(shí)例:
puts Object.superclass
# ---- 輸出結(jié)果 ----
BasicObject
2. 類實(shí)際上是對象
現(xiàn)在讓我們實(shí)例化Person
,獲得一個(gè)person對象。還記得class
方法嗎,它是獲取一個(gè)對象所屬的類的方法。
實(shí)例:
person = Person.new
puts person.class
puts Person.class
# ---- 輸出結(jié)果 ----
Person
Class
解釋:第一條輸出結(jié)果代表了person對象所屬于Person
類,同理,第二條輸出結(jié)果代表了Person
類,是Class
類的對象。
Tips:每一個(gè)類都是Class的實(shí)例,這也意味著Class類也是Class類自己的實(shí)例。
實(shí)例:
puts Class.class
# ---- 輸出結(jié)果 ----
Class
因此綜上所述,所有的類都是對象。
Tips:Class的超類是Module,所以可以說所有的類其實(shí)也是模塊。
實(shí)例:
puts Class.superclass
puts Module.superclass
# ---- 輸出結(jié)果 ----
Module
Object
3. 祖先鏈
由之前的學(xué)習(xí),我們知道了Ruby的方法都是定義在類中,調(diào)用方法前,Ruby會(huì)在對象的類中查找到那個(gè)方法,現(xiàn)在讓我們學(xué)習(xí)一下它的運(yùn)作原理。
舉一個(gè)例子:
"hello".reverse
在這里,"hello"調(diào)用了reverse方法,"hello"這個(gè)字符串對象作為調(diào)用方法的對象被稱為接收者(receiver)。
現(xiàn)在我們在Person
中定義方法。
實(shí)例:
class Person
def age
18
end
def name
"Andrew"
end
end
class Student < Person
end
student = Student.new
puts student.name
puts student.age
# ---- 輸出結(jié)果 ----
Andrew
18
解釋:剛剛這個(gè)例子表示了一個(gè)簡單的繼承關(guān)系。Ruby在查找方法時(shí)遵循一個(gè)先右一步再向上的原則,如剛剛的student對象,Ruby會(huì)先找到他的類Student
(class
方法),從這個(gè)類中沒有找到方法,然后繼續(xù)向上找它的超類(superclass
方法),從Person
中找到了age
和name
。
這種方法查找順序的機(jī)制,我們可以使用祖先鏈(ancestors)來表示。
實(shí)例:
p Student.ancestors
# ---- 輸出結(jié)果 ----
[Student, Person, Object, Kernel, BasicObject]
解釋:我們可以看到,祖先鏈?zhǔn)菑膕tudent的類逐級向上查找的,需要注意的是,Kernel
是一個(gè)模塊,因?yàn)?code>Object類include了Kernel
模塊,所以Kernel
也會(huì)出現(xiàn)在祖先鏈里面。
在之前的學(xué)習(xí)中我們了解到,在方法名稱相同的時(shí)候,include
模塊不會(huì)覆蓋類原本的方法,而Ruby 2.0以后的perpend
方法會(huì)覆蓋掉類原本的方法。因此使用include
或prepend
引入模塊會(huì)出現(xiàn)兩種不同的祖先鏈。
實(shí)例:
module A
end
module B
end
class Person
prepend A
include B
end
class Student < Person
end
p Student.ancestors
# ---- 輸出結(jié)果 ----
[Student, A, Person, B, Object, Kernel, BasicObject]
解釋:從比較容易理解的層面上,因?yàn)槟KA的優(yōu)先級比Person
更高,模塊B的優(yōu)先級比Person
低,所以當(dāng)查到Student
之后,先會(huì)去找模塊A,然后是Person
,然后是模塊B …
4. 小結(jié)
本章節(jié)中的重點(diǎn)是:
- 所有的類都是
Object
的子類。(Ruby 1.8 - 1.9 是BasicObject
); - 所有的類都是
Class
的實(shí)例,包括它本身; - 所有對象的方法都在類中,根據(jù)祖先鏈來進(jìn)行查找;
- 模塊會(huì)根據(jù)
include
和perpend
出現(xiàn)在祖先鏈中不同的位置上。