14 Ruby 的類
我們在之前的章節講了 Ruby 的很多物件,學會了如何使用簡單的物件(例如:數字和字串)以及資料結構陣列和雜湊來完成一些工作,瞭解如何使用方法,做好了充足的準備。本章中,我會為大家講解 Ruby 的類,如何建立一個類以及類的例項,以及類的例項方法如何建立。
1. 什麼是 Ruby 的類
當 Ruby 執行程式的時候,會建立一個空間,我們使用具體的事物對這些空間進行填充,我們可以呼叫這些事物的方法去做某些事情。同時,每個具體的事物(物件)都是一般思想或型別的例項,這些思想稱為類。
在終端中我們可以通過class
方法來檢視物件所屬的類。
例項:
"Hello World".class
# ---- 輸出結果 ----
String
物件是類的具體例項(表現)。
我們還可以使用is_a?
的方法來具體詢問物件是否屬於某個類:
例項:
"Hello World".is_a?(String)
# ---- 輸出結果 ----
true
類是用於物件的藍圖。類具有很多特性,每個類定義了許多方法,這些方法特定用於此類事物(例如:字串),每次從類建立物件的時候,每個物件都會擁有類給他們的這些方法,也可以說,物件從類繼承了方法。
2. 一步一步建立類
2.1 定義一個類
首先讓我們建立一個Calculator
的類,並逐步為它新增方法。
例項:
class Calculator
end
解釋:我們使用關鍵字class
end
定義一個類。
注意事項:我們定義類的時候,首字母一定要大寫開頭,否則會出現報錯。同樣對於由幾個單詞命名的類,我們應該使用大寫字母分隔這些單詞,例如:RubyStudyGroup
,而對於變數名和方法名,我們要使用下劃線,且所有內容都應該小寫,例如:local_variable
,method_name
。
2.2 建立一個例項
因為我們定義了一個完整的有效類,所以我們可以建立一個Calculator
例項,下面是建立一個Calculator
例項的方法。
Calculator.new # ---- 輸出結果 ---- #<Calculator:0x00007fb4132c0af0>
解釋:new
方法是在類Calculator
上定義的(Calculator
它本身既是一個類也是一個物件,還記得我們說Ruby中所有東西都是物件嗎。所以它也可以擁有方法)。此方法建立了一個新的例項,並返回它。
格式#<...>
告訴您此物件不是簡單的東西,比如數字,字串或陣列。它只是告訴您類的名稱,Calculator
以及Ruby分配給該物件的內部ID。
每個物件都有自己唯一的內部物件ID,當我在計算機上執行此程式碼時,Ruby分配的ID為0x00007fb4132c0af0
。如果您執行它,將會得到不同的結果。實際上,在大多數情況下,您可以忽略此ID。另外,我們可以檢查我們的新計算器例項確實是類Calculator
的例項。
例項:
class Calculator
end
calculator = Calculator.new
puts calculator.class
puts calculator.is_a?(Calculator)
# ---- 輸出結果 ----
Calculator
true
2.3 定義例項方法
我們可以在類和物件上定義和呼叫方法。類上可用的方法稱為類方法,例項上可用的方法稱為例項方法。剛才我們定義了一個類,現在讓我們為它增加一個sum
方法。
例項:
class Calculator
def sum(number, other)
number + other
end
end
解釋:我們在類中定義一個方法,即為這個類的例項方法。
注意事項:我們在定義例項方法的時候記得要縮排 2 個空格,表明sum
屬於Calculator
類。這是一種規範。
那麼我們如何使用這個定義的sum
例項方法呢?
我們可以例項化一個Calculator
,然後呼叫這個方法。
例項:
calculator = Calculator.new
puts calculator.sum(2, 3)
# ---- 輸出結果 ----
5
2.4 初始化物件
在我們向類新增任何行為(方法)之前,我們希望能夠為其提供一些初始資料。
讓我們重新定義一個Person
類。
例項:
class Person
end
在我們的情況下,我們希望該人知道自己的名字。
例項:
class Person
def initialize(name)
end
end
解釋:您會看到我們為這個類增加了一個名為initialize
的方法,並且可以接收一個name
引數。當類方法new
建立物件時,將在內部呼叫特殊的initialize
方法。
我們可以通過以下這種方式將名稱傳遞給類的內部:
p Person.new("Andrew")
# ---- 輸出結果 ----
#<Person:0x00007fb41326b118>
2.5 使用例項變數記錄初始化屬性
繼續我們剛才做的事,我們想在例項化物件的時候,讓物件保留自己初始化的名稱,這時我們用到了例項變數(instance variable)
例項:
class Person
def initialize(name)
@name = name
end
end
解釋:我們將name
的值賦予了例項變數@name
,它的作用域為整個在物件範圍內的任何位置。
此時我們再次例項化Person
類:
p Person.new("Andrew")
#---- 輸出結果 ----
#<Person:0x00007fb41321add0 @name="Andrew">
此時,我們建立的Person
物件中有了一個例項變數@name
,它的值為Andrew
。
2.6 屬性讀取器(getter)
我們已經建立了一個名為 Andrew 的Person
物件,那麼如何獲取它的名字呢。
例項:
class Person
def initialize(name)
@name = name
end
def name
@name
end
end
此時我們可以通過向物件傳送name
的訊息,獲取對應的資訊。
例項:
person = Person.new("Andrew")
person.name
#---- 輸出結果 ----
"Andrew"
解釋:我們定義了一個方法name
,它返回了例項變數@name
,由此建立了一個屬性讀取器。屬性讀取器返回例項變數的值,也可以說,屬性讀取器公開了例項變數,讓所有的人都可以讀取它。
除此之外我們還有一種簡單的寫法,實現@name
的讀取:
class Person
attr_reader :name
def initialize(name)
@name = name
end
end
和上面的操作是等效的。
2.7 屬性設定器(setter)
現在我們新增一個功能,一個人不僅要有名稱,也要能設定密碼,這個密碼我們希望在Person
物件被例項後才被告知,讓我們進一步改變這個類。
class Person
def initialize(name)
@name = name
end
def name
@name
end
def password=(password)
@password = password
end
end
解釋:如您所見,方法password =
只能執行一個引數(稱為password
),並將此區域性變數的值分配給例項變數@password
,其他什麼也不做。
現在讓我們為一個Person
物件新增密碼。
例項:
person = Person.new("Andrew")
person.password=("super password")
p person
#<Person:0x00007fb413154810 @name="Andrew", @password="super password">
解釋:在幕後,Ruby 在執行程式碼時將person.password ="something"
行轉換為person.password =("something")
,這僅呼叫方法password=
,在右側傳遞的值作為引數,這只是另一種方法。
同樣我們也有一種簡寫方法給屬性設定器:
class Person
attr_writer :password
def initialize(name)
@name = name
end
def name
@name
end
end
2.8 物件作用域
您可以通過物件上的任何方法訪問:
-
所有區域性變數;
-
所有例項變數;
-
所有物件的方法。
讓我們寫一個greet
方法來為您展示。
例項:
class Person
def name
@name
end
def greet(other)
name = other.name
puts "Hi " + other_name + "! My name is " + @name + "."
end
end
boy = Person.new("Andrew")
girl = Person.new("Alice")
boy.greet(girl)
#---- 輸出結果 ----
Hi Alice! My name is Andrew.
解釋:這是一個物件互動的例子,我們定義了一個greet
方法,這個裡面呼叫了@name
例項變數,other
是一個引數,代表Person
的物件。
注意事項:當 Ruby 找到一個識別符號時,Ruby 首先尋找一個區域性變數,然後尋找一個方法。上述程式碼name
沒有呼叫例項方法name
而是取的other.name
的值就是因為這個原因。
2.9 self
每個物件都通過呼叫 self 的方式以每種方法認識自己。這是 Ruby 中的一個特殊關鍵字,它的意思是物件本身。
class Person
def initialize(name)
@name = name
p self
end
end
person = Person.new("Anja")
p person
#---- 輸出結果 ----
#<Person:0x007f9994972428 @name="Anja">
#<Person:0x007f9994972428 @name="Anja">
解釋:如您所見,我們兩次輸出相同的物件。一次在初始化方法中使用p self
,一次在外部範圍中使用p person
。您還可以看到,這兩個例項的神祕物件 ID 相同。因此我們可以知道它確實是同一物件。
所以,之前的方法我們可以這樣修改:
class Person
def name
@name
end
def greet(other)
name = other.name
puts "Hi " + name + "! My name is " + self.name + "."
end
end
boy = Person.new("Andrew")
girl = Person.new("Alice")
boy.greet(girl)
#---- 輸出結果 ----
Hi Alice! My name is Andrew.
解釋:現在,我們再次在兩個不同的物件上呼叫方法name
。當 Ruby 看到self
時,它知道我們正在引用當前的Person
物件,並在其上呼叫方法name
。
注意事項:self
是一個關鍵字並不是一個方法,我們從下面的程式碼可以證明。
person = Person.new("Andrew")
p person.self
#---- 輸出結果 ----
NoMethodError (undefined method `self' for #<Person:0x00007fb413290bc0 @name="Andrew">)
關鍵字是在 Ruby 中具有特殊含義的單詞,例如class
,def
,end
和self
。
2.10 類方法(Class Method)
類方法的格式為:類名.方法名()
,通俗來說就是類名呼叫的方法,在其他語言中也稱為靜態方法(Static Method)。
現在我們要輸出Person
類英語和中文名稱,對Person
類要這樣修改。
例項:
class Person
def self.cn_name
'人'
end
def self.en_name
'Person'
end
end
puts Person.cn_name
puts Person.en_name
#---- 輸出結果 ----
人
Person
解釋: 上述我們定義了兩個類方法,一個為cn_name
,一個為en_name
,我們使用Person.cn_name
和Person.en_name
來呼叫這兩個方法。
除此之外,我們還可以使用另外一種寫法:
class Person
class << self
def cn_name
'人'
end
def en_name
'Person'
end
end
end
puts Person.cn_name
puts Person.en_name
解釋:這種寫法在單件類章節中會詳細講解,可以將它看成在一個類中定義多個類方法的一種便捷辦法。
假如我們要在類的外面定義類方法還可以這樣寫:
class Person
end
def Person.cn_name
'人'
end
def Person.en_name
'Person'
end
puts Person.cn_name
puts Person.en_name
#---- 輸出結果 ----
人
Person
Tips:建立了一個類的時候,類方法和類的作用域內(非例項方法部分),self表示的是類本身。
例項:
class Person
puts "in class: #{self}"
puts self == Person
def self.cn_name
puts "in class method: #{self}"
puts self == Person
end
end
#---- 輸出結果 ----
in class: Person
true
in class method: Person
true
3. 小結
本章中我們學習瞭如何建立一個 Ruby 的類,如何定義一個類、建立一個例項、定義例項方法、初始化物件、使用例項變數記錄、初始化屬性、屬性讀取器、屬性設定器、物件作用域以及瞭解了 self 的含義。