Subclassing and @@variable

I have the following test code:

class XSuperClass
#@@data = “This is the super class.”

def callme
    puts @@data
end

end

class XSubClass < XSuperClass
@@data = "This is the sub class."
end

x = XSubClass.new
x.callme

If I un-comment out the second line and run it, I get this:

This is the sub class.

If I comment out the second line and run it, I get this:

supersub.rb:5:in `callme’: uninitialized class variable @@data in XSuperClass (NameError)

Why is the presence of the second line important? The value that it
sets seems to be irrelevant.

Philip Mak wrote:

class XSuperClass
#@@data = “This is the super class.”

def callme
    puts @@data
end

end

Why is the presence of the second line important? The value that it
sets seems to be irrelevant.

Because class variables are some kind of wierd black magic. Once a class
variable comes into existance, it is available to all instances of the
class and subclasses. I.e. it is available downwards in the inheritance
hierarchy.

When you don’t make the @@data variable until the subclass, it does not
propagate back up to the superclass. Beware, this path may lead to
madness:

class Dad
def dad
puts @@data
end
end

class Son < Dad
@@data = "Son data"
def son
puts @@data
end
end

class Dad
@@data = "Dad data"
end

s = Son.new
s.son #=> "Son data"
s.dad #=> “Dad data”

···


([ Kent Dahl ]/)_ ~ [ http://www.stud.ntnu.no/~kentda/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 5. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

So I see…

— begin classvar.rb —
class Parent
@@name = "I’m the parent."
def initialize
puts @@name
end
end

class Son < Parent
@@name = "I’m the son."
end

class Daughter < Parent
@@name = "I’m the daughter."
end

Son.new # => I’m the daughter.
Daughter.new # => I’m the daughter.
— end —

This behavior is downright counterintuitive to me. The class
"Daughter" has managed to affect the behavior of the class “Son”, but
"Daughter" is not an ancestor of “Son”.

What would you guys suggest I do if I want a variable set in the
parent that the child may or may not override? So far, I’ve had to
resort to using instance variables instead, like this:

class Parent
def initialize
@name = "I’m the parent."
end
end

class Son
def initialize
super
@name = "I’m the son."
end
end

class Daughter
def initialize
super
@name = "I’m the daughter."
end
end

This doesn’t seem to be the right way to do things though, since with
the way I’m using @name, it’s a variable whose value will be the same
across all instances of the same class.

Thoughts?

···

On Mon, Aug 12, 2002 at 02:05:45AM +0900, Kent Dahl wrote:

Because class variables are some kind of wierd black magic. Once a class
variable comes into existance, it is available to all instances of the
class and subclasses. I.e. it is available downwards in the inheritance
hierarchy.

When you don’t make the @@data variable until the subclass, it does not
propagate back up to the superclass. Beware, this path may lead to
madness:

Philip Mak wrote:

This behavior is downright counterintuitive to me. The class
"Daughter" has managed to affect the behavior of the class “Son”, but
"Daughter" is not an ancestor of “Son”.

The fact that you define the classvariable in Parent suggest that all
subclasses of Parent should have access to one and the same variable.
There is no way for Ruby to know wheter you want to make your own class
variable or reuse the parents when you simply assign to it.

What would you guys suggest I do if I want a variable set in the
parent that the child may or may not override?

I take it you mean that the overriding change should only propagate
downwards, as if you had defined the class variable at that particual
subclass?

Depending on what you wish to do with the variable, I’d suggest:

  • Make a separate class variable in the subclasses that is assigned the
    variable of the parents class variable to start with:
    class Son < Dad
    @@son_data = @@data

    use @@son_data only

    end
  • Use instance methods. Instance methods are made for overriding, class
    variables not so.
  • Use real class instance variables instead of class variables. (Might
    be hard to do if you still want to propagate the variables downwards,
    since a subclass is a totally different object.)

But it really depends alot on what you want to be able to do with the
variable.

···


([ Kent Dahl ]/)_ ~ [ http://www.stud.ntnu.no/~kentda/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 5. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

This may or may not be what you are looking for, using class instance
variables. (The eval bit is just for conveninece.)

def class_attr_reader name
eval <<-EOF
def self.class_#{name}
return @#{name}
end
EOF
end

class Parent
class_attr_reader :info
@info = "parent"
def initialize
puts "Parent 1: Initializing #{(self.class).class_info}"
puts "Parent 2: in #{Parent.class_info}"
end
end

class Daughter < Parent
@info = "daughter"
def initialize
puts Daughter.class_info

super

end
end

class Son < Parent
@info = "son"
def initialize
puts Son.class_info
super
end
end

Parent.new
puts "—"
Daughter.new
puts "—"
Son.new

···

On Tue, 13 Aug 2002, Philip Mak wrote:

What would you guys suggest I do if I want a variable set in the
parent that the child may or may not override? So far, I’ve had to
resort to using instance variables instead, like this:

What would you guys suggest I do if I want a variable set
in the parent that the child may or may not override?

> Depending on what you wish to do with the variable, I'd suggest:

[… several suggestions elided …]
> - Use real class instance variables instead of class variables. (Might
> be hard to do if you still want to propagate the variables downwards,
> since a subclass is a totally different object.)

A class instance variable is just a variable that belongs to the class
object rather than an instance of the class. You have to do a little
work to reference a class instance variable from an object of that
class (setting up a class method is sufficient, perhaps there is an
easier way).

Elaborating on this suggestion, you could do the following …

class Parent
  @@cv = "Parent Class Variable"
  @civ = "Parent Class Instance Variable"
  def show_cv
    puts @@cv
  end
  def show_civ
    self.type.show_civ
  end
  def self.show_civ
    puts @civ
  end
end

class Son < Parent
  @@cv = "Son Class Variable"
  @civ = "Son Class Instance Variable"
  def self.show_civ
    puts @civ
  end
end

class Daughter < Parent
  @@cv = "Daughter Class Variable"
  @civ = "Daughter Class Instance Variable"
  def self.show_civ
    puts @civ
  end
end

puts "Son ..."
s = Son.new
s.show_cv
s.show_civ

puts
puts "Daughter ..."
d = Daughter.new
d.show_cv
d.show_civ

The output is …

Son ...
Daughter Class Variable
Son Class Instance Variable

Daughter ...
Daughter Class Variable
Daughter Class Instance Variable

Notice that the class instance variable is unique to each subclass and
is not shared in the class heirarchy like the @@ class variable.

···


– Jim Weirich jweirich@one.net http://w3.one.net/~jweirich

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

– Nikodemus

···

On Tue, 13 Aug 2002, Kent Dahl wrote:

I take it you mean that the overriding change should only propagate
downwards, as if you had defined the class variable at that particual
subclass?

  • Use instance methods. Instance methods are made for overriding, class
    variables not so.
  • Use real class instance variables instead of class variables. (Might
    be hard to do if you still want to propagate the variables downwards,
    since a subclass is a totally different object.)

But it really depends alot on what you want to be able to do with the
variable.


([ Kent Dahl ]/)_ ~ [ http://www.stud.ntnu.no/~kentda/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 5. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)


I refuse to have a battle of wits with an unarmed person.

Hi –

def class_attr_reader name
eval <<-EOF
def self.class_#{name}
return @#{name}
end
EOF
end

class Parent
class_attr_reader :info
@info = "parent"
def initialize
puts "Parent 1: Initializing #{(self.class).class_info}"
puts "Parent 2: in #{Parent.class_info}"
end
end

class Daughter < Parent
@info = "daughter"
def initialize
puts Daughter.class_info

super

end
end

It’s probably better not to hardcode the class’s name into its method,
in case there’s a GrandDaughter some day :slight_smile: You could do:

puts type.class_info

or, if you move that up into Parent, you can just have:

class Parent
class_attr_reader :info
@info = "parent"
def initialize
puts type.class_info
puts "Parent 1: Initializing #{(self.class).class_info}"
puts "Parent 2: in #{Parent.class_info}"
end
end

class Daughter < Parent
@info = "daughter"
end

class Son < Parent
@info = "son"
end

David

···

On Tue, 13 Aug 2002, Nikodemus Siivola wrote:


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

Hi –

A class instance variable is just a variable that belongs to the class
object rather than an instance of the class. You have to do a little
work to reference a class instance variable from an object of that
class (setting up a class method is sufficient, perhaps there is an
easier way).

Elaborating on this suggestion, you could do the following …

class Parent

[…]

  def self.show_civ
    puts @civ
  end
end

class Son < Parent

[…]

  def self.show_civ
    puts @civ
  end
end

No need to redefine show_civ in the children, is there?

Just for fun, here’s a slight variant on your example, using a
technique to create the class’s instance variable which for some
reason I’ve always found rather neat:

class Parent
  class << self; attr_reader :civ; end
  @@cv = "Parent Class Variable"
  @civ = "Parent Class Instance Variable"
  def show_civ
    puts type.civ
  end
  def show_cv
    puts @@cv
  end
end

class Son < Parent
  @@cv = "Son Class Variable"
  @civ = "Son Class Instance Variable"
end

class Daughter < Parent
  @@cv = "Daughter Class Variable"
  @civ = "Daughter Class Instance Variable"
end

(Uh oh, I might end up back at the self.writer=(x) thread if I go much
further… :slight_smile:

David

···

On Tue, 13 Aug 2002, Jim Weirich wrote:


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

No need to redefine show_civ in the children, is there?

Hmmm … I was sure I tried it without the redefinition and it didn’t
work. But you are correct, they aren’t needed.

BTW, I like the use of the attr_reader in the class. Good move.

···


– Jim Weirich jweirich@one.net http://w3.one.net/~jweirich

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)