Reputation: 87
I am Learning ruby and the concept of self
is very confusing to me in this example.
class Alpha
def initialize
self.A
end
def self.A
puts "A"
end
def self.B
self.A
puts "B"
end
end
that when I tried to create a new instance from the class Alpha
Alpha.new
I got NoMethodError
but when I called B
method, it executed normally.
Alpha.B
# A
# B
#=>nil
Upvotes: 1
Views: 53
Reputation: 110685
First define the class Alpha
as follows.
class Alpha
puts "self in Alpha = #{self}"
def a
puts "self in a = #{self}"
end
def Alpha.b
puts "self in b = #{self}"
end
def Alpha.c
puts "self in c = #{self}"
end
end
# self in Alpha = Alpha
We have one instance method, Alpha#a
and two class methods, Alpha::b
and Alpha::b
. (Referencing instance and class methods that way is a Ruby convention, one you will see all the time in the docs.)
Alpha.methods(false)
#=> [:c, :b]
Alpha.instance_methods(false)
#=> [:a]
See Object::methods and Module#instance_methods, noting the implications of including the argument false
.
Instance methods are invoked on instances of the class; class methods are invoked on the class.
alpha = Alpha.new
#=> #<Alpha:0x000059c5ff614f08>
alpha.a
# self in a = #<Alpha:0x000059c5ff614f08>
Alpha.b
# self in b = Alpha
Alpha.c
# self in c = Alpha
What if we try to invoke an instance method on the class or a class method on an instance?
Alpha.a
#=> NoMethodError (undefined method `a' for Alpha:Class)
alpha.b
#=> NoMethodError (undefined method `b' for #<Alpha:0x000059c5ff614f08>)
We would normally write the class as follows.
class Alpha
def a
puts "self in a = #{self}"
end
def self.b
puts "self in b = #{self}"
end
def self.c
puts "self in c = #{self}"
end
end
Within the class definition self
is Alpha
, which is why the two ways of writing the class are equivalent. The only reason for using self
is that if we decide to later change the name of the class to Beta
we won't have to change all the Alpha
's to Beta
in the class method definitions.
If we want to invoke the class method Alpha::b
from the instance method Alpha#a
we must change the latter as follows:
class Alpha
def a
puts "self in a = #{self}"
self.class.b
end
end
alpha.a
# self in a = #<Alpha:0x000059c5ff614f08>
# self in b = Alpha
Here self.class
returns Alpha
, and we know what Alpha.b
returns.
Let's change Alpha#a
once again and add a second instance method:
class Alpha
def a
puts "self in a = #{self}"
d
end
def d
puts "self in d = #{self}"
end
end
alpha.a
# self in a = #<Alpha:0x000059c5ff614f08>
# self in d = #<Alpha:0x000059c5ff614f08>
I could have written:
def a
puts "self in a = #{self}"
self.d
end
but self.
is not necessary and generally is not included. The reason is that when a method has no explicit receiver, as d
in:
def a
puts "self in a = #{self}"
d
end
self
becomes the default receiver. We know from the discussion above that within the instance method definition self
equals the instance alpha
.
Upvotes: 1
Reputation: 2488
initialize
operates in the instance context, not class context. If you want to call class method, you need to self.class.A
. Or you can try to go e bit more crazy and overwrite class new
method:
class Alpha
def self.new
self.A
super # this actually calls initialize after object is created
end
[...]
end
Upvotes: 1