ruby 類別變數

Posted by Noel on 2015-07-22

類別變數,有時也稱靜態變數,簡單說就是專屬於類別的變數,不依實體不同而有所差異,類別成員下皆共享的,因為是存在特定的記憶體區塊,所以不會因實體的存活期間所影響。

各個 oop 語言的類別變數大致的理念都是相同的,用法上可能會略有不同,今次是就來探討ruby的使用狀況。

類別變數種類

ruby的類別變數有區分以下兩種,而兩者都可以被類別方法正常存取與使用,但還是有差異如下:

@@var => Class Variable 類別變數

@var => Class Instance Variable 類別實體變數

主要差異為:

1.@@可以給子類別繼承; @不可以

類別變數繼承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Parent
@@blood = :b
@hobby = :car

def self.blood
@@blood
end

def self.hobby
@hobby
end
end

class Child < Parent
end

Parent.hobby # car
Child.hobby # nil
Parent.blood # b
Child.blodd # b

Child.instance_eval do
@hobby = :coding
end
Child.hobby # coding

2.@@雖可以被繼承,但由於所有類別都共用,故@@更改後也會影響其他類別所擁有的相同@@;而@因為不會被繼承所以沒這問題

所以使用@@的時候請小心,一般來說較常使用@來當做類別變數

類別變數共享問題
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Parent
@@blood = :b

def self.blood
@@blood
end

def self.blood=(value)
@@blood = value
end
end

class Child < Parent
end

Parent.blood # b
Child.blood # b
Child.blood = :c
Parent.blood # c
Child.blood # c

3.@@可以給實體方法使用; @不可以

因為@對實體方法的角度來看,會當做實體變數去讀取而非類別實體變數

實體方法使用類別變數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Parent
@@blood = :b
@hobby = :car

def blood
@@blood
end

def hobby
@hobby
end
end

dad = Parent.new
dad.blood # b
dad.hobby # nil

如果實體方法想使用類別實體變數,那就改讓實體方法去呼叫類別方法即可

1
2
3
4
5
6
7
8
9
10
11
12
13
Parent.class_eval do
def self.hobby
@hobby
end

def get_hobby_by_class_method
Parent.hobby
end
end

mom = Parent.new
mom.hobby # nil
mom.get_hobby_by_class_method # car

補充

換種類別方法定義的方式來看更複雜一點的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Animal
class << self
@@move = true
@@breath = "Air"
@food = %w(meat grass)
def description
if @@move
puts "We Can beathe #{@@breath}"
end
end

def food
@food ||= []
end

def food_list
@food.each do |food|
puts "We like #{food}"
end if @food
end
end
end

Animal.description
# We Can beathe Air
# nil
Animal.food_list
# nil
Animal.food << "meat"
Animal.food_list
# We like meat

看來我們無法在class << self的裡面直接定義@的類別實體變數,若非要在該區塊內設定@的類別實體變數,得借用類別方法來完成!


參考

Class Variables and Methods