類別是物件導向程式設計中的核心概念。 在 Crystal 中,萬物皆為物件,每個物件都有型別,並且能回應某些方法。 類別是建立物件的藍圖,為狀態(例如變數)提供初始值,並提供行為的實作(例如方法)。 物件是類別的實例,持有所有實例變數與狀態。
使用class關鍵字定義類別,後面接著以UpperCamelCase撰寫的類別名稱。
class Account
end
若要建立類別的實例,請使用new方法呼叫類別名稱。
class Account
end
account = Account.new
在上面的範例中,變數account是類別Account的實例。
建立實例時,一個名為initialize的特殊方法會扮演建構子的角色。
建立實例時會呼叫它。
建構子讓你設定實例的初始狀態。
initialize方法可以接受引數,並在建立實例時傳入。
class Account
def initialize(number)
5 + number # => 9
end
end
Account.new(4)
initialize方法無法手動回傳值,而是由new回傳新建立的實例。
class Account
def initialize(number)
5 + number
end
end
Account.new(4)
# => #<Account:0x7f5dc33dcea0>
實例方法是可供類別實例使用的方法,並且可以在該實例上呼叫。
實例方法使用def關鍵字定義,後面接著方法名稱。
它們定義在類別定義內。
class Account
def balance
100
end
end
account = Account.new
account.balance # => 100
實例變數是實例中所有方法都能使用的變數。
若要存取實例變數,你需要定義一個實例方法。
它們可以在initialize方法中初始化。
它們使用@前綴定義。
class Account
def initialize
@balance = 100
end
def balance
@balance
end
end
account = Account.new
account.balance
# => 100
實例變數也可以定義在類別定義內。
class Account
@age = 0
end
實例變數可以使用傳入initialize方法的引數來初始化。
由於 Crystal 無法推斷初始化時所用引數的型別,因此必須明確指定。
如果你想進一步了解,可以閱讀:型別推斷。
若要指定變數的型別,可以使用:符號,後面接著型別。
例如,如果你想建立一個型別為Int32、名為balance的實例變數,可以這麼做:@balance : Int32。
或者,如果你想建立一個型別為Bool、名為raining的實例變數,可以這麼做:@raining : Bool。
有幾種實作方式,其中一種是在initialize方法中將引數宣告為該型別,再將實例變數指定給該引數,如下面的範例:
class Account
def initialize(balance : Int32)
@balance = balance
end
def balance
@balance
end
end
account = Account.new(1)
account_2 = Account.new(5)
account.balance
# => 1
account_2.balance
# => 5
另一種方式是在類別定義中,以下列形式宣告實例變數的型別:@<variable> : <Type>,如下面的範例:
class Weather
@raining : Bool
def initialize(raining)
@raining = raining
end
def raining
@raining
end
end
weather = Weather.new(true)
weather.raining
# => true
方法可以修改實例變數。 這些方法可以在類別的實例上呼叫。 當方法修改實例變數時,該變更只會在呼叫它的那個類別實例中生效。
class Account
def initialize(balance : Int32)
@balance = balance
end
def withdraw(amount)
@balance -= amount
end
def balance
@balance
end
end
account = Account.new(10)
account_2 = Account.new(10)
account.balance
# => 10
account_2.balance
# => 10
account.withdraw(5)
account.balance
# => 5
account_2.balance
# => 10
類別方法是為類別定義、而非為其實例定義的方法。
它們提供了一種建立不依賴實例狀態之方法的方式。
它們使用def關鍵字定義,後面接著self.<method name>。
self是對呼叫 self 時所在命名空間的參照。
在這個例子中,它指的是類別Account。
這等同於撰寫Account.,但self.是較建議的寫法。
class Account
def self.balance
100
end
end
Account.balance
# => 100
你的朋友 Johannes 很喜歡自己動手做(DIY)硬體。 他們剛做出了一台果汁機,Johannes 非常興奮。 Johannes 的目標是每天早上都能做出新鮮的果汁。
Johannes 果汁機有很多厲害的功能。 像是內建的測量系統,可以告訴你杯子裡有多少果汁。 它也有計時器,能告訴你機器已經運轉了多久。
Johannes 不太擅長寫程式,但他們想寫一個程式來控制這台機器。 因此他們請你來幫忙。
這台機器有一個除錯燈,原理很簡單:只要機器有電,除錯燈就會亮。 只有在除錯燈亮著的時候,程式才會執行。
這個方法應該是一個名為JuiceMaker的類別方法。
定義類別方法JuiceMaker.debug_light_on?,讓它回傳true。
JuiceMaker.debug_light_on?
#=> true
當機器通電時,應該會建立一個JuiceMaker類別的實體。
這台機器應該要有一個初始化狀態。
初始化狀態應該包含以下資訊:
false。定義初始化狀態,它會把杯子裡的果汁量當作參數,並建構這個初始化狀態。
初始化狀態應該包含一個設為false的實體變數@running,以及一個根據類別初始化時所給的引數而定的實體變數@fluid。
JuiceMaker.new(5)
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=5>
這台機器可以開啟和關閉。
你需要定義一個方法來開啟機器。
當機器開啟時,運轉狀態應該設為true。
定義方法JuiceMaker#start,用來開啟機器。
juice_maker = JuiceMaker.new(5)
juice_maker.start
juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=true, @fluid=5>
這台機器需要能告訴你機器的狀態。
如果機器正在運轉,它應該回傳true;如果沒有運轉,則回傳false。
定義方法JuiceMaker#running?,讓它回傳機器的狀態。
juice_maker = JuiceMaker.new(5)
juice_maker.start
juice_maker.running?
#=> true
這台機器可以把果汁加入杯子。 機器能夠知道加了多少果汁到杯子裡。 加入果汁之後,機器需要協助才能知道杯子裡有多少果汁。
定義方法JuiceMaker#add_fluid,它會把加入的果汁量當作參數,並更新杯子裡的果汁量。
juice_maker = JuiceMaker.new(5)
juice_maker.add_fluid(5)
juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=10>
這台機器可以開啟和關閉。
你需要定義一個方法來關閉機器。
這個方法只會在機器運轉時被呼叫。
當機器關閉時,運轉狀態應該設為false。
關閉之後,機器也需要協助才能判斷杯子裡有多少果汁。
這台機器每分鐘會用掉五個單位的果汁。
定義方法JuiceMaker#stop,它會把機器已運轉的分鐘數當作參數,並回傳機器關閉後杯子裡的果汁量。
juice_maker = JuiceMaker.new(5)
juice_maker.start
juice_maker
#=> <JuiceMaker:0x10f0b8 @running=true, @fluid=5>
juice_maker.stop(1)
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=0>