Ruby中的继承方式是单继承,如果确实有需要从多个类继承(JAVA有接口的概念,C++中有抽象类),可以使用module,在类中将module mixin。ruby中的继承很简单,如下代码,B从A类继承:
class A
def initialize
puts "A init."
end
end
class B < A
def initialize
puts "B init."
end
end
注意其中的一个问题,在C++中,在子类的默认构造函数会自动调用父类的默认构造函数,但是在ruby中不会这样。以上代码。使用B.new时,输出:B init. 如果要在子类中指定使用父类的函数,可以使用super关键字。如下,修改后调用父类的构造函数:
class A
def initialize
puts "A init."
end
end
class B < A
def initialize
puts "B init."
super
end
end
我们知道类也是对象,类有一个隐藏的metaclass. 那么定义在metaclass中的方法是否会被继承呢?示例代码如下:
class A
class << self
def google(something)
puts "google #{something}"
end
end
def initialize
puts "A init."
end
end
class B < A
def initialize
puts "B init."
super
end
end
B.google("ruby") #=> google ruby
puts A.singleton_methods #=> google
puts B.singleton_methods #=> google
可以看到A的类方法也被子类继承了,再看以下代码,可以想到他的输出是什么嘛?
class A
class << self
def google(something)
puts "google #{something}"
end
end
def initialize
puts "A init."
end
self.google("c++")
end
class B < A
def initialize
puts "B init."
super
self.class.google("rails")
end
self.google("java")
end
B.new
在解释器运行到A的类定义的时候,此时的self是A,所以会调用A.google,输出"c++",当解释器运行到B类的定义时,此时的self对象是B,运行B.google,输出:"java",然后到建立B类的对象时,会调用B#initialize函数,输出,“B init." ,super关键字,将调用父类的构造函数,输出"A init.",最后,在B#initialize方法中,self指向B的对象,self.class则是指B,self.class.google("rails")则相当于B.google("rails"),输出:"rails",完整的输出如下:
google c++
google java
B init.
A init.
google rails
有了上面的经验,那么再看一个例子,考虑它的输出:
class A
class << self
attr_accessor :testvalue
end
def initialize
puts "A init."
end
self.testvalue = "abc"
end
class B < A
def initialize
puts "B init."
super
puts self.class.testvalue
end
end
B.new
输出如下:
B init.
A init.
此处并未输出"abc",如果按照我们前面的经验attr_accessor建立的两个类函数testvalue 和testvalue=应该被继承到B类中,那么在B类中调用B.testvalue会输出"abc",但实际上却并未输出, 奇怪,这是为什么呢?
看到这个问题,第一个反映是否和attr_accessor这个元方法有关系。改造一下,不使用attr_accessor,那么结果会怎么样,尝试,代码如下:
class A
class << self
def testvalue
@testvalue
end
def testvalue=(val)
@testvalue = val
end
end
def initialize
puts "A init."
end
self.testvalue = "abc"
end
class B < A
def initialize
puts "B init."
super
puts self.class.testvalue
end
end
B.new
运行输出结果和上一个版本是一样的,分析原因问题可能是出在变量@testvalue上。修改上述代码,在其中加入日志:
class A
class << self
def testvalue
puts "get @testvalue=#{@testvalue}"
@testvalue
end
def testvalue=(val)
@testvalue = val
puts "assign @testvalue=#{@testvalue}"
end
# attr_accessor :testvalue
end
def initialize
puts "A init."
end
self.testvalue = "abc"
end
class B < A
def initialize
puts "B init."
super
puts self.class.testvalue
end
end
B.new
运行结果如下:
assign @testvalue=abc
B init.
A init.
get @testvalue=
在B#initialize中的self.class.testvalue => B.testvalue(),在A类的赋值self.testvalue="abc" =>A.testvalue=("abc"),testvalue函数中@testvalue变量默认是针对self而言的,显然,B.testvalue() 和A.testvalue=("abc"),两个调用的self对象不一样,这就造成了为什么没有B#initialize中的self.class.testvalue没有值的原因。