Monday, June 19, 2006

Ruby Class Variables, Attributes and Constants

Ruby Class Variables, Attributes and Constants

(For a related post, see Use Class Instance Variables Not Class Variables)


Writing a little ruby code the other day, I wanted to use a class variables but it didn't behave as expected.


01: class Class_Variable
02: @@var = 1
03: def Class_Variable.report; @@var end
04: end
05:
06: class Child_V < Class_Variable
07: @@var = 2
08: end
09:
10: puts Class_Variable.report #=> 2
11: puts Child_V.report #=> 2
12:


I was surprised by the result. Most other languages would have the child class shadow the parent field, but Ruby shared it! Ruby does provide Class Attributes and Constants as alternatives, but each has it's own symantics.

I threw together the following code to show the differences.


01: class Class_Variable
02: @@var = 1
03: def Class_Variable.report; @@var end
04: end
05:
06: class Child_V < Class_Variable
07: @@var = 2
08: end
09:
10: puts Class_Variable.report #=> 2
11: puts Child_V.report #=> 2
12:
13:
14:
15: class Class_Attribute
16: @var = 1 #class attribute
17:
18: def initialize
19: @var = 2 #instance attribute
20: end
21:
22: def report
23: @var # instance attribute, not the class attribute
24: end
25:
26: def Class_Attribute.report
27: @var # class attribute
28: end
29: end
30:
31: class Child_A < Class_Attribute
32: @var = 3
33: end
34:
35: puts Class_Attribute.report #=> 1
36: puts Class_Attribute.new.report #=> 2
37: puts Child_A.report #=> 3
38: puts Child_A.new.report #=> 2
39:
40:
41:
42: class Class_Constant
43: VAR = [ 'a' ]
44: VAR2 = [ 'b' ]
45:
46: def self.report
47: VAR2[0]
48: end
49: end
50:
51: class Child_C < Class_Constant
52: VAR2 = [ 'c' ]
53: end
54:
55: puts Class_Constant::VAR[0] #=> 'a'
56: puts Class_Constant::VAR2[0] #=> 'b'
57: puts Class_Constant.report #=> 'b'
58: #puts Child_C::VAR[0] #=> uninitialized constant error
59: puts Child_C::VAR2[0] #=> 'c'
60: puts Child_C.report #=> 'b'
61:


First notice that Class Variables are shared with their subclass (lines 1-11) . This differs greatly from Java and C# which shadow inherited variables.

The Class Attributes and Constants are "class private". That is, the child has no access to the parent attribute/constant with the same name. Class methods however are inherited. So calling "report" for Child_A and Child_C displays a significant difference between using Class Attributes and Constants (lines 37 and 60).

Unfortunately none of these alternatives matches the behavior seen in other languages. This can cause some confusion when going from Java or C# to Ruby. Class Attributes using accessor methods is the closest match to other languages. However the syntax similarities between class attributes and instance attributes can cause problems (lines 16, 19, 23, 27).

No comments: