模块方法作用域与内部类扩展疑问:include后为何内部类可调用模块方法?
CLogic.execute会执行Ma#execute而不是报错? 你的疑惑点很典型,其实问题的根源并不是include Ma让CLogic类被模块Ma扩展了,而是Ruby里顶级作用域include的特殊行为,加上方法查找机制在起作用,咱们一步步拆解:
首先,当你在顶级作用域(也就是
main对象的上下文)执行include Ma时,Ruby会把这个模块直接包含进Object类里。这是Ruby的一个特殊处理——因为main是Object的实例,顶级的include操作默认会把模块方法注入到Object的实例方法集合中。接下来,你要明白:在Ruby里,所有类本身都是
Class类的实例。比如CLogic这个类,它其实是Class的一个实例,而Class类本身又继承自Object类。所以CLogic作为Object的间接实例,自然会继承Object类的所有实例方法——包括刚才从Ma模块注入的execute方法。当你调用
CLogic.execute时,你并不是在调用CLogic的类方法,而是在给CLogic这个对象(没错,类本身也是对象)发送execute消息。Ruby的方法查找路径是这样的:- 先找
CLogic的 eigenclass(类方法的存储地),没找到execute; - 接着找
Class类的实例方法,还是没有; - 再往上找
Object类的实例方法,这里就找到了来自Ma的execute方法,于是就执行它了。
- 先找
举个直观的例子验证:当你执行完include Ma后,试试调用"test string".execute,你会发现同样返回"Module Ma executes."——因为字符串也是Object的实例,同样继承了这个方法。
如果想要达到你预期的报错效果,你需要确保execute方法不会被注入到Object类中。比如可以把Ma模块包含到某个特定类里,而不是顶级作用域;或者把execute定义成模块的类方法,而不是实例方法。
内容的提问来源于stack exchange,提问作者J.Doe

