为什么Ruby类变量表现得如此奇怪?

时间:2014-01-03 20:27:07

标签: ruby

我只是花了很多时间在IRB(好吧,PRY实际上)试图弄清楚类变量在Ruby中是如何工作的,我完全被我发现的东西困惑了。

从我所见过的(如果我错了,请纠正我),类变量在类,这些类的实例,子类和子类实例之间共享。但是,对于子类及其实例,如果已在超类中分配了类变量,则只将类变量共享给超类。如果它还没有被分配到超类中,那么它在那里保持未定义,直到它被分配给变量变为共享的那一点......到底是什么? (如果您感到困惑,请参阅下面的示例。)

那为什么呢?我听说Ruby类变量基于Smalltalk中的一些类似概念,但我真的不知道为什么这种行为是可取的。


示例:

superfoo_and_subbar.rb

class SuperFoo
  def class_var_x=(x)
    @@x = x
  end
  def class_var_x
    @@x
  end
end

class SubBar < SuperFoo
  # Define these again, just in case they're bound at compile time or something...
  def class_var_x=(x)
    @@x = x
  end
  def class_var_x
    @@x
  end
end

PRY(或IRB)会话中:

# Okay, let's do this!

require './superfoo_and_subbar' # => true

SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo
SubBar.new.class_var_x # NameError: uninitialized class variable @@x in SubBar

# Okay, no suprise there, let's define the class variable on SuperFoo
SuperFoo.new.class_var_x = 1
SuperFoo.new.class_var_x # => 1

# Okay, looks about right. What does bar say now?

SubBar.new.class_var_x # => 1

# Okay, that's pretty weird but I did hear that Ruby class variables behave
# that way, so no big deal.

SubBar.new.class_var_x = 2
SubBar.new.class_var_x # => 2
SuperFoo.new.class_var_x # => 2

# Right, so these both point to the same variable then.

新PRY会议:

# Now let's try this again:

require './superfoo_and_subbar' # => true

SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo
SubBar.new.class_var_x # NameError: uninitialized class variable @@x in SubBar

# So far so good. Let's set x on SubBar first this time

SubBar.new.class_var_x = 2
SubBar.new.class_var_x # => 2

# Okay, so x is now set on SubBar so it should also be set on SuperFoo then, right?

SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo

# Wait, what? So they're seperate variables now?

SubBar.new.class_var_x # => 2
SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo

# It certainly looks that way. What happens if I set x on SuperFoo then?

SuperFoo.new.class_var_x = 3

SuperFoo.new.class_var_x # => 3
SubBar.new.class_var_x # => 3

# Wait, so now they're the same varaible again? What the heck?

SubBar.new.class_var_x = 4

SuperFoo.new.class_var_x # => 4
SubBar.new.class_var_x # => 4

# ...WHY!?!? Seriously, what's the point of this?

2 个答案:

答案 0 :(得分:2)

这就是继承的工作方式。是的类变量与其子类共享,一旦超类将定义它。 SubBar.new.class_var_x仅在类SubBar中创建了类变量。很明显,它的超类不会有任何访问权限。

class Foo
  def x_class_var=(val)
    @@x =val
  end
  def x_class_var
    @@x
  end
end

class Bar<Foo
  def x_class_var=(val)
    @@x =val
  end
  def x_class_var
    @@x
  end
end

Foo.class_variables # => []
Bar.class_variables # => []
Bar.new.x_class_var = 10
Foo.class_variables # => []
Bar.class_variables # => [:@@x]

Bar.new.x_class_var@@x类中创建了类变量Bar,而Foo的超级类Bar将无法访问此@@x Foo.class_variables # => [] Bar.class_variables # => [] Foo.new.x_class_var = 10 Foo.class_variables # => [:@@x] Bar.class_variables # => [:@@x] 1}}。现在看另一种方式 -

@@x

现在Foo已在Foo类中创建,因此作为Bar的子类,@@x可以访问{{1}}。这是因为类变量可以从超类到子类共享,而不是从子类到超类,如上例中所示。

答案 1 :(得分:1)

在第一个PRY会话中,当您选中SubBar.new.class_var_x时,它会检查@@class_var_x是否存在。由于它SuperFooSubBar < SuperFoo确实存在,因此SuperFoo @@class_var_x

在第二个会话中,当您选中SuperFoo.new.class_var_x时,没有查看SubBar的{​​{1}},因为父类的对象不在关心从它们继承的类,因此它创建了@@class_var_x的新实例。