1. 程式人生 > Ruby 程式語言入門 >14 Ruby 的類

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_variablemethod_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 中具有特殊含義的單詞,例如classdefendself

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_namePerson.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 的含義。