Treatment of class variables in inherited classes


(Gray, Jeff) #1

This question probably has more to do with the fundamentals of OOP than Ruby
specifically, but I’m looking for an explanation of why the behavior of
class variables in inherited classes is the way it is. Consider:

···

class Box

@@stuff = {:e => 1} # intentionally commented out

def Box.add(k, v)
@@stuff[k] = v
end
def Box.
@@stuff[knobname]
end
end

class Crate < Box
@@stuff = {:e => 2}
end

class Suitcase < Box
@@stuff = {:e => 3}
end

Crate.add(:d, 4)
Suitcase.add(:d, 5)
puts Crate[:d] # -> 4 [would be 5 if line 2 were uncommented]
puts Suitcase[:d] # -> 5
puts Crate[:e] # -> 2 [would be 3 if line 2 were uncommented]

So when a class variable is defined in a parent class, all inherited classes
see the same object. More specifically, the most recent definition of that
class variable is used, even if it occurred in a subclass. Yet when a class
variable is not initialized in the parent class, the variable points to
different objects in each class. This is sensible, but it feels like there
is lacking a “have your cake and eat it too” mechanism that allows for
inherited classes to override/redefine class variables without affecting the
ancestor and sibling classes.

So what are the guiding principles of inheritance that account for this
behavior?

Thanks,

  • jeff

(David Alan Black) #2

Hello –

This question probably has more to do with the fundamentals of OOP than Ruby
specifically, but I’m looking for an explanation of why the behavior of
class variables in inherited classes is the way it is. Consider:


class Box

@@stuff = {:e => 1} # intentionally commented out

def Box.add(k, v)
@@stuff[k] = v
end

[…]

Crate.add(:d, 4)

Don’t you get an “uninitialized class variable” error at that point
(if line 2 is commented out)?

So when a class variable is defined in a parent class, all inherited classes
see the same object. More specifically, the most recent definition of that
class variable is used, even if it occurred in a subclass. Yet when a class
variable is not initialized in the parent class, the variable points to
different objects in each class. This is sensible, but it feels like there
is lacking a “have your cake and eat it too” mechanism that allows for
inherited classes to override/redefine class variables without affecting the
ancestor and sibling classes.

So what are the guiding principles of inheritance that account for this
behavior?

I think you’re right that class variables are essentially
per-hierarchy (rather than per class) objects. Also that if you
define them first in a subclass, this doesn’t propagate upward (which
Matz referred to in ruby-talk:19774 as “an error that Ruby does not
detect yet”, so I would treat it as deprecated behavior).

If you want real per-class variables, you can create instance
variables for your classes:

class A
@var = "I am A’s instance variable."
def A.speak
puts @var
end
end

class B < A
puts @var # nil, because @var is an instance variable of
# a different object (namely A)
end

This is different from a class variable, but possibly useful in that
it does provide per-class behavior.

David

···

On Wed, 3 Jul 2002, Gray, Jeff wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav