Ruby类常量和继承之谜

时间:2016-07-09 08:08:50

标签: ruby class constants immutability

为什么以下两个代码段不会产生相同的输出? push|=之间的差异非常棘手。我认为|=作为一项任务可能会有所作为?最重要的是,常量实际上是安全的,以后不会改变,我猜不是吗?

代码来自this question的答案。你可以看到它in action here

class LibraryItem

  ATTRIBUTES = ['title', 'authors', 'location']

end

class LibraryBook < LibraryItem

  ATTRIBUTES.push('ISBN', 'pages']

end

puts LibraryItem::ATTRIBUTES
puts LibraryBook::ATTRIBUTES

> ["title", "authors", "location", "ISBN", "pages"]
> ["title", "authors", "location", "ISBN", "pages"]

class Foo

  ATTRIBUTES = ['title','authors','location']

end

class Bar < Foo

  ATTRIBUTES |= ['ISBN', 'pages']

end

puts Foo::ATTRIBUTES
puts Bar::ATTRIBUTES

> ["title", "authors", "location"]
> ["title", "authors", "location", "ISBN", "pages"]

2 个答案:

答案 0 :(得分:4)

在第一个示例中,ATTRIBUTES引用相同的数组,并且您正在修改它。因此,

puts LibraryItem::ATTRIBUTES
puts LibraryBook::ATTRIBUTES

产生相同的结果。

在第二种情况下,您正在执行a |= b,这是a = a | b的简写。这将为类ATTRIBUTES创建一个名为Bar的新数组。因此,

puts Foo::ATTRIBUTES
puts Bar::ATTRIBUTES

产生不同的结果。

您可以在此问题中详细了解 Ruby Assignment Operator Ruby |= assignment operator

修改

Ruby数组使用&|运算符实现了一小组set操作。

单个管道,|执行联合操作,即仅添加唯一元素。

a = [:foo, :bar, :baz]
a |= [:baz, :buz] # => [:foo, :bar, :baz, :buz]

答案 1 :(得分:3)

ruby​​中的常量有点用词不当。重新分配常量会产生警告:

Foo=1
Foo=2
(irb):5: warning: already initialized constant Foo

但是没有什么能阻止你改变push所做的实际值本身。如果你想防止这种情况发生,那么你可以冻结数组,即

class LibraryItem    
  ATTRIBUTES = ['title', 'authors', 'location'].freeze
end

尝试改变数组现在会引发异常。虽然只有数组被冻结,所以你可以做类似

的事情
LibraryItem::ATTRIBUTES.first.upcase!

(假设您没有打开冻结的字符串文字)并且允许更改。除了单独冻结字符串(或者在ruby 2.3及以上版本上打开该文件的冻结字符串文字)之外,我不知道其他方法