为什么特异类(eigenclass)看起来和self.class很相似,但却不等同?

86

我好像错过了某些信息,请你为我解释一下。

为什么对象的特殊类(eigenclass)和self.class不同呢?

class Foo
  def initialize(symbol)
    eigenclass = class << self
      self
    end
    eigenclass.class_eval do
      attr_accessor symbol
    end
  end
end

我的逻辑是将特异类与class.self等同起来:

class << self是声明类方法而不是实例方法的一种方式。它是def Foo.bar的一种快捷方式。

因此,在引用类对象时,返回self应该与self.class相同。这是因为class << self将为定义类方法/属性设置selfFoo.class

我是不是只是被搞糊涂了?还是说这是Ruby元编程中的一个诡计?

3个回答

124

class << self不仅仅是一种声明类方法的方式(虽然它可以被用来这样做)。你可能已经看到了一些用法,例如:

class Foo
  class << self
    def a
      print "I could also have been defined as def Foo.a."
    end
  end
end

这段代码是可行的,等价于def Foo.a,但其工作方式有些微妙。秘密在于,在该上下文中,self指的是对象Foo,它的类是一个唯一的、匿名的Class子类。这个子类被称为Fooeigenclass。因此,def aFooeigenclass中创建了一个名为a的新方法,可以通过常规的方法调用语法Foo.a访问。

现在让我们看一个不同的例子:

str = "abc"
other_str = "def"

class << str
  def frob
    return self + "d"
  end
end

print str.frob # => "abcd"
print other_str.frob # => raises an exception, 'frob' is not defined on other_str

这个例子与上一个相同,但一开始可能很难看出。`frob` 不是在 `String` 类上定义的,而是在 `str` 的特有匿名子类——即 `str` 的特殊类——的元类上定义的。因此,`str` 有一个 `frob` 方法,但一般的 `String` 实例没有。我们也可以覆盖 `String` 的方法(在某些棘手的测试场景中非常有用)。
现在我们能够理解你最初的例子了。在 `Foo` 的初始化方法内部,`self` 指的不是类 `Foo`,而是某个特定的 `Foo` 实例。它的元类是 `Foo` 的子类,但它不是 `Foo`;否则,我们在第二个例子中看到的技巧就行不通了。因此,继续你的例子:
f1 = Foo.new(:weasels)
f2 = Foo.new(:monkeys)

f1.weasels = 4 # Fine
f2.monkeys = 5 # Also ok
print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method.

21
每个实例的都是所创建类的一个匿名子类。f1的类是Foo的一个匿名子类,Foo的类是Class的一个匿名子类。 - David Seiler
6
谢谢你的赞扬 :)很多人对这个问题理解没有像你这样清晰明了。 - horseyguy
所以f1的类不是Foo,而是Foo的一个匿名子类?但是,当我在f1上调用方法时,它不是要在Foo中查找该方法吗?还是先在Foo的匿名子类中查找,然后在Foo中查找? - berto77
3
f1的特征类(eigenclass)在概念上与f1实例有何不同?如果只有f1实例可以访问其特征类的方法,那么f1和其特征类之间的区别不是有点模糊了吗? - elju
1
@elju 是的,有点儿。真正重要的区别在于“Foo”和“f1的特异类”之间;如果你明白了这点,那你可能就没问题了。 - David Seiler
显示剩余2条评论

50
最简单的回答是:特殊类无法实例化。
class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class

1
你在这个网站上可能只有一个点,但我喜欢你和你的风格。 - horseyguy
同意楼上的观点,这是一个很好的答案。 - Christopher Scott
3
如果已经阅读了@DavidSeiler上面的回答,这则评论极具洞察力和帮助性。 - Jazz
Theo Power 在这里演示了引发的异常。 - New Alexandria

11

7
字幕还是......微妙之处? ;) - horseyguy

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