Ruby中的继承的一个问题

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没有值的原因。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值