这是您的代码,稍作修改。
class Parent
def self.create_singleton_variable
@arr = [1,2,3]
end
def self.arr
puts "self = #{self} in the getter for @arr"
@arr
end
end
class Child < Parent
def get
puts self.arr
end
def self.get
puts self.arr
end
end
我已经写了
Parent
以更传统的方式。除了
puts
声明,它等同于问题中包含的内容。
首先,拍头:
Kernel#puts
-有退货吗
nil
. 你需要移除
放
从两种方法:
class Child < Parent
def get
self.arr
end
def self.get
self.arr
end
end
Parent.create_singleton_variable
#=> [1, 2, 3]
Child.get.nil?
self = Child in the getter for @arr
#=> true
我们看到了这个
arr
,由
Child
类方法
get
,
self
等于
孩子
,因此该方法查找类实例变量
@arr
属于
孩子
不是
起源
. 由于没有初始化此类实例变量,
零
返回。
您需要以下内容。
class Parent
class << self
def create_singleton_variable
@arr = [1,2,3]
end
def arr
puts "self = #{self} in the getter for @arr"
@arr
end
end
end
class Child < Parent
def get
self.class.superclass.arr
end
def self.get
superclass.arr
end
end
与问题中给出的不同之处在于
Class#superclass
更改范围(即,
自己
到
起源
.
我们看到期望的结果已经得到了。
Child.get
self = Parent in the getter for @arr
#=> [1, 2, 3]
Child.new.class.superclass.arr
self = Parent in the getter for @arr
#=> [1, 2, 3]
一个常见的误解是
孩子
类方法已定义
def self.get; self.arr; end
调用getter
Parent::arr
,因此返回
起源
的实例变量
阿尔
. 它是
Child::arr
但是,该方法是从
起源
它就是
孩子
的类实例变量
阿尔
这是一个微妙但重要的区别。
编辑2
第一个观察是
起源
可以用更传统(完全等效)的方式编写。
class Parent
def self.create_singleton_variable
@arr = [1,2,3]
puts "arr is initialized #{@arr}"
end
def self.arr
puts "arr is #{@arr.inspect}"
@arr
end
end
不管怎么写,
自己
意志平等
起源
当任何一个类方法在父类上被调用时。因此,第一个将创建类实例变量
阿尔
.
Parent.methods(false)
#=> [:create_singleton_variable, :arr]
Parent.instance_variables
#=> []
Parent.ancestors
#=> [Parent, Object, Kernel, BasicObject]
现在让我们为
起源
.
Parent.create_singleton_variable
# arr is initialized [1, 2, 3]
Parent.instance_variables
#=> [:@arr]
现在让我改变
阿尔
.
Parent.instance_variable_set(:@arr, ['dog', 'cat'])
#=> ["dog", "cat"]
Parent.arr
# arr is ["dog", "cat"]
#=> ["dog", "cat"]
接下来,创建类
孩子
,但不要预先准备模块。
class Child < Parent
create_singleton_variable
arr
end
arr is initialized [1, 2, 3]
arr is [1, 2, 3]
Child.ancestors
#=> [Child, Parent, Object, Kernel, BasicObject]
Child.instance_variables
#=> [:@arr]
Child.instance_variable_get(:@arr)
#=> [1, 2, 3]
没有什么惊喜。接下来加载模块。
module CompletionGeneration
def self.prepended(base)
base.singleton_class.prepend(ClassMethods)
end
module ClassMethods
def completion
puts "self=#{self}"
puts "superclass=#{superclass}"
puts "self.class=#{self.class}"
puts "self.class.superclass == #{self.class.superclass}"
puts "superclass.arr == #{superclass.arr.inspect}"
puts "self.class.superclass.arr == #{self.class.superclass.arr}"
rescue Exception => e
# do nothing, this is just so you can see arr is actually
# initialized in the context of the Child
puts "Exception => e=#{e}"
end
end
end
(音符
self.
不需要在
"superclass.arr == #{superclass.arr.inspect}"
)现在将此模块预先准备到
起源
.
Parent.prepend CompletionGeneration
Parent.ancestors
#=> [CompletionGeneration, Parent, Object, Kernel, BasicObject]
Parent.methods.include?(:completion)
#=> true
Child.ancestors
#=> [Child, CompletionGeneration, Parent, Object, Kernel, BasicObject]
Child.methods.include?(:completion)
#=> true
回调模块方法
CompletionGeneration::prepended
被解雇
base
等于
起源
造成
起源
的singleton类
ClassMethods
,从而添加类方法
Parent::completion
. 自从
起源
以前没有使用该名称的方法
prepend
或
include
会有同样的效果。更进一步,而不是
Parent.singleton_class.include ClassMethods
,可以使用
included(base)
改为回调,并执行
Parent.extend ClassMethods
. 也许
预置
这里是一个普通的案例
起源
可能具有该名称的类方法。
一
现在执行以下操作。
Child.completion
self=Child
superclass=Parent
self.class=Class
self.class.superclass == Module
arr is ["dog", "cat"]
superclass.arr == ["dog", "cat"]
Exception => e=undefined method `arr' for Module:Class
在以下情况下引发异常:
puts "self.class.superclass.arr == #{self.class.superclass.arr}"
正在执行。相当于
puts "self.class.superclass.arr == #{Module.arr}"
但是当然
Module
没有模块方法
ARR
.
1鉴于
Child.ancestors
准备
起源
模块的唯一原因
起源
孩子们
包括
(而不是
预置
)模块;也就是说,如果子级已经有了方法
completion
在预处理之前,该方法不会被模块的方法以相同的名称抢先。