【问题标题】:Can Ruby subclass instance variables _overwrite_ the superclass's (same name)?Ruby 子类实例变量可以_overwrite_ 超类的(同名)吗?
【发布时间】:2013-01-10 07:47:06
【问题描述】:

在“ruby 编程语言”一书的第 7.3.5 章“继承和实例变量”中说:

因为与实例变量无关 继承,因此子类使用的实例变量不能“隐藏” 超类中的实例变量。如果子类使用具有相同的实例变量 名称作为其祖先之一使用的变量,它将覆盖其>祖先的值 多变的。这可以有意地改变祖先的行为,或者它可以 不经意间就完成了。在后一种情况下,几乎可以肯定会导致错误。与 继承前面描述的私有方法,这也是为什么它只是 当您熟悉(并控制)Ruby 类时,可以安全地扩展 超类的实现。

我做了自己的测试,但似乎子类中的实例变量不会影响超类

我的 Ruby 版本

bob@bob-ruby:~$ ruby --version
ruby 1.9.3p194 (2012-04-20 revision 35410) [i686-linux]
bob@bob-ruby:~$ 

下面是代码

class Point
   attr_accessor :x,:y
   def initialize(x,y)
     @x,@y=x,y
   end
end

class Point3D < Point
   attr_accessor :x,:y,:z
   def initialize(x,y,z)
     @x=x
     @y=y
     @z=z
   end
end

irb(main):052:0> p=Point.new(1,2)
=> #<Point:0x87e8968 @x=1, @y=2>
irb(main):053:0> q=Point3D.new(4,5,6)
=> #<Point3D:0x87e423c @x=4, @y=5, @z=6>
irb(main):054:0> q.x
=> 4
irb(main):055:0> p.x
=> 1
irb(main):056:0> 

【问题讨论】:

  • 嗯,这些是“实例”变量,这里有 2 个类的 2 个独立实例,你期望什么?
  • 是的,这就是我的困惑。因为书上说它会覆盖超类的值,书错了吗?
  • @ailms 不,你错误地认为当你有一个类的两个实例时,在一个类中设置一个实例变量会影响另一个。书上说,如果你在超类中设置值(如在ctor中)然后在子类中设置它,子类的值获胜。
  • @Dave Newton :我不认为有两个 same 类的实例。我有两个不同类的 2 个实例。这本书说它将改变超类的行为,但是如果子类的实例变量与超类的实例变量没有关系,CAN它'改变行为'并导致错误?
  • @ailms 出于空间和格式原因将评论移至答案。

标签: ruby inheritance subclass instance-variables superclass


【解决方案1】:

这本书(强调和补充我的):

如果 子类 使用与其祖先之一使用的 [n 实例] 变量同名的 instance 变量,它将覆盖其祖先的值变量。

我知道你没有同一个类的两个实例;我们正在专门讨论继承。

当子类使用与超类使用的实例变量同名的实例变量时,只有一个实例变量。如果子类更改了该实例变量的值,并且超类访问它,它会获取子类设置的值。

当一个子类被实例化时,它“好像”它是也是超类的一个实例。 Ruby 的实现方式意味着如果超类有一个实例变量@foo,子类可以访问它。这使得子类的@foo 和超类的@foo 之间的区别毫无意义。

这就是子类可以改变超类行为的方式:通过设置超类可能使用的值。如果子类设置@foo = 42,并且超类方法访问@foo,它会看到42。这可能是有意的,也可能不是有意的,因此发出警告。这可能会导致令人沮丧的调试会话。

class MyStack
  def initialize
    @my_array = []
  end

  def push(item)
    @my_array << item
  end
end

# Stack class that keeps a list 
# of every item ever pushed.
class TrackingStack < MyStack
  def initialize
    super
    @my_array = []
  end

  def push(item)
    super
    @my_array << item
  end

  def all_items_ever_pushed
    @my_array
  end
end

TrackingStack 引入了一个错误,因为它无意中使用了与用于保存堆栈内容的超类数组相同的名称。如果您不熟悉超类的实现,这会导致混乱和错误,直到您深入挖掘以了解意外行为的来源。

超类的一个实例就是:超类的一个instance,说子类的一个instance是什么意思是没有意义的> 会影响它,因为它们完全不相关。

这里是一个改写:

当您无法控制或不熟悉超类的实现时,子类化可能会有风险。一个原因是因为在子类中引入实例变量可能会覆盖超类实例变量的值,从而导致意外行为。

【讨论】:

  • 为什么是单实例变量?我不明白,你的意思是子类“Point3D”中的@x,@y 是超类“Point”中的同一个实例变量?你能在答案中写一些代码吗? tks
  • @ailms 这正是(咳咳)要点:假设Point3D 对@x 和@y 进行了某种转换,因为它是3D 的。如果Point 的行为未预料到该转换,Point 的行为将以一种可能不受欢迎的方式发生变化。我添加了一个更透明的示例。
  • 我想我明白了。 'alter the behavior'这个词意味着当子类中的'super'方法访问实例变量时,它会看到来自子类实例的值,而不是超类的实例变量的值。对吗? p.s:第二堂课好像没有“
  • 我运行代码(添加了
  • @ailms 它可能看起来是对的,直到您在超类MyStack 中有一个修改@my_array 的pop。要修复实现,您需要在 TrackingStack 中使用不同的数组。您需要在push 中使用super 才能共享超类的行为,这就是子类化的重点。
【解决方案2】:

实例变量属于实例,而不属于类。 “继承”的整个概念在这里甚至没有意义,继承只适用于类。

【讨论】:

  • 是的,这就是我的困惑。因为书上说它将覆盖超类的值,书错了吗?还是我误解了什么?
  • 可能不是开始谈论类实例变量的最佳时机;)
猜你喜欢
  • 2015-10-07
  • 2015-08-13
  • 2014-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-18
  • 1970-01-01
  • 2019-02-19
相关资源
最近更新 更多