Reputation: 11704
The ruby class-instance stuff is giving me a headache. I understand given this...
class Foo
@var = 'bar'
end
...that @var
is a variable on the created class's instance.
But how do I create a sub-class overridable class variable?
Here is an example of what I would do in Python:
class Fish:
var = 'fish'
def v(self):
return self.var
class Trout(Fish):
var = 'trout'
class Salmon(Fish):
var = 'salmon'
print Trout().v()
print Salmon().v()
Which outputs:
trout
salmon
How do I do the same thing in ruby?
Upvotes: 22
Views: 35683
Reputation: 23990
@var
mentioned above is called class instance variable, which is different from instance variables... read the answer here to see the diff.
Anyway this is the equivalent Ruby code:
class Fish
def initialize
@var = 'fish'
end
def v
@var
end
end
class Trout < Fish
def initialize
@var = 'trout'
end
end
class Salmon < Fish
def initialize
@var = 'salmon'
end
end
puts Trout.new.v
puts Salmon.new.v
Upvotes: 8
Reputation: 1050
There is a one problem: you can override @var:
Salmon.var = 'shark' will override @var, so
puts (Salmon.new).v # => shark
Upvotes: 1
Reputation: 5128
It's a common mistake made by Java coders coming to Ruby as well, and one of the big conceptual jumps I had to get my head around. At first it seems odd, but it's really one of the cooler aspects of Ruby -- all code is executable, including class definitions.
So, instance variables must be declared inside methods. It has to do with how 'self' is evaluated. 'self' is the current object. The interpreter will lookup method calls and variable references first in 'self':
class Fish
@var = "foo" # here 'self' == Fish, the constant which contains the class object
def foo
# do foo
end
end
fish = Fish.new
fish.foo # here 'self' == fish, an instance of Fish
In a class definition, 'self' is set to be the class object being defined, so any references within a class definition will refer to that class object, in this case Fish.
When a method is called on an instance of Fish, however, self is set to be the receiver of the call, the particular instance of Fish. So outside of a method definition, self is the class object. Inside a method, self is the instance of the receiver. This is why @var outside of a method definition is more like a static variable in Java, and @var inside a method definition is an instance variable.
Upvotes: 2
Reputation: 246774
To contrast @khelll's answer, this uses instance variables on the Class objects:
class Fish
# an instance variable of this Class object
@var = 'fish'
# the "getter"
def self.v
@var
end
# the "setter"
def self.v=(a_fish)
@var = a_fish
end
end
class Trout < Fish
self.v = 'trout'
end
class Salmon < Fish
self.v = 'salmon'
end
p Trout.v # => "trout"
p Salmon.v # => "salmon"
Edit: to give instances read-access to the class's instance variable:
class Fish
def type_of_fish
self.class.v
end
end
p Trout.new.type_of_fish # => "trout"
p Salmon.new.type_of_fish # => "salmon"
Upvotes: 21
Reputation: 11704
Here's the version I ended up figuring out using hobodave's link:
class Fish
class << self
attr_accessor :var
end
@var = 'fish'
def v
self.class.var
end
end
class Trout < Fish
@var = 'trout'
end
class Salmon < Fish
@var = 'salmon'
end
puts (Trout.new).v # => trout
puts (Salmon.new).v # => salmon
Notice that subclassing only requires adding an @var
-- no need to override initialize.
Upvotes: 4