Ruby特殊类(eigenclass)的意外行为

4

首先,让我们添加一个方法来检索从此博客文章复制的特殊类。

class Object 
  def eigenclass 
    class << self
      self
    end 
  end 
end

然后创建一个简单的类。
class A
end

puts A.new.eigenclass.superclass      # => A
puts Class.new.eigenclass.superclass  # => #<Class:Object>

我期望第二个puts输出 Class
有什么线索可以解释这发生的原因吗?
3个回答

4
通过puts A.new.eigenclass.superclass,你实际上是在调用类A的一个实例的#eigenclass方法。我将从背景故事开始解释eigenclass的工作原理,然后再告诉你在代码中发生了什么。
背景故事: EigenClass是一个隐藏的类,其中包含仅适用于该特定对象的单例方法。
所以对于obj = Foo.new,类层次结构实际上看起来像:
obj --eigenclass--> #> --(superclass)--> A
而不是:
obj --(class)--> A
当你使用#eigenclass时,会生成一个隐藏类,这个类是通过劫持self获取的。
现在,在Ruby中,Class是一个对象。这也意味着#eigenclass也应该显示A的隐藏eigenclass(其中包含A的单例方法)。
A --(eigenclass)--> # --(superclass)--> #
现在它显示#而不是A是因为Ruby以一种非常美丽的模式组织类、超类和eigenclasses。这可以通过示例来展示,而不是引用混淆的语言:
A.superclass #=> Object   
A.eigenclass #=> #<Class: A>   
A.eigenclass.superclass #=> #<Class: Object> => Eigenclass of Object   
A.eigenclass.superclass == Object.eigenclass #=> true   

一个类的元类的超类是原始类的超类的元类。
现在来看你的情况:Class.new.eigenclass.superclass,这已经不言自明了。 Class.new 对应一个新的匿名类,比如说B,然后你在它上面调用了 eigenclass.superclass。由于B的超类是Object,所以B的元类的超类是超类的元类。
我尽力用例子解释了一下,请在下面的评论中进一步澄清;我会相应地更新答案。补充(来自Pragmatic MR):Shot from Pragmatic MR
在上面的图中,D继承自C。因此,D.eigenclass.superclass是(superclass of D)[即C]的元类。现在C的超类是Object.. 这就是同样的逻辑。
谢谢!

我认为你的意思是A.eigenclass.superclass == Object.eigenclass。 - bazaretas
是的,抱歉。不小心漏掉了那个。 - kiddorails
我喜欢你把USB电缆放置成一个棒人欣赏你的画作的方式。+1(是为了答案,而不是棒人)。 - Cary Swoveland

4

从那篇博客文章中,你可以构建一个类似的图表:

                        +------------------+               +-------------------+
                        |      Object      |- eigenclass ->| Object eigenclass |
                        +------------------+               +-------------------+
                                 ^                                   ^             
                                 | superclass             superclass |                                                     
                        +------------------+               +-------------------+
                        |        A         |- eigenclass ->|    A eigenclass   |
                        +------------------+               +-------------------+
                                 ^
                                 | superclass
+-------+               +------------------+                                   
| A.new |- eigenclass ->| A.new.eigenclass |                                   
+-------+               +------------------+    

尝试查找实例的元类的超类,显示其指向 A 类。
A.new.eigenclass.superclass      # => A                               

Class.new会返回一个Class对象的实例,即一个新类。它就像A类一样也是一个类。

Class.new.class # => Class
A.class         # => Class

A类和Class.new类的超类都是隐式的Object
Class.new.superclass # => Object
A.superclass         # => Object

因为A的超类是Object,所以A的特殊类(eigenclass)的超类是Object的特殊类。
Object.eigenclass                            # => #<Class:Object>
A.eigenclass.superclass                      # => #<Class:Object>
A.eigenclass.superclass == Object.eigenclass # => true

同样地,找到Class.new的元类的超类将得到Object的元类。
Class.new.eigenclass.superclass              # => #<Class:Object>

Class.newA.new 的区别在于,Class.new 是一个类,因此可以构造新的对象,而 A.new 不行。

Class.new.new # => #<#<Class:0x007f86b50d8f70>:0x007f86b50d8f20>
A.new.new     # => NoMethodError: undefined method `new' for #<A:0x007f86b50cbf50>

两个 new 方法是完全不同的。Class.new 是一个静态方法,而 A.new 则是 Class 类中的实例方法。http://ruby-doc.org/core-2.0/Class.html#method-c-new - bazaretas

1
一个类的元类(eigenclass)拥有一整个阴影层次结构的元类,这些元类都在该类的祖先的元类和Class之间。这是因为期望类继承其祖先的类方法。例如,如果你定义了def Numeric.kind_of_number?() true end,你会期望Fixnum.kind_of_number?也是true。所以你需要将Numeric的元类作为Fixnum的元类的祖先。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接