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.
···
“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)
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 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…
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.
···
“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)