【问题标题】:Class Variables Overriding类变量覆盖
【发布时间】:2021-08-24 23:33:52
【问题描述】:

我在玩类变量,我知道我可以覆盖子类中的类变量,但我没有意识到我在哪里调用类方法很重要。例如;

class Vehicle
  @@wheels = 4

  def self.wheels
    @@wheels
  end
end

puts Vehicle.wheels #4
puts Vehicle.wheels #4
puts Vehicle.wheels #4

class Motorcycle < Vehicle
  @@wheels = 2
end

puts 语句输出 4,这对我来说很有意义,因为我认为在从 Motorcycle 类实例化对象之前,您的 @@wheels 不会被覆盖。于是我就这样做了:

class Vehicle
  @@wheels = 4

  def self.wheels
    @@wheels
  end
end

class Motorcycle < Vehicle
  @@wheels = 2
end

puts Vehicle.wheels #2
puts Vehicle.wheels #2
puts Vehicle.wheels #2

现在,一旦我将这三行移到 Motorcycle 类之后,它们就会输出 2... 即使 Motorcycle 对象尚未实例化。这让我很困惑。详细来说这里发生了什么?是因为当我们尝试访问它时加载了类变量吗?这怎么可能?

【问题讨论】:

  • “直到从 Motorcycle 类实例化一个对象”——你能澄清一下吗?您永远不会在上面的代码 sn-ps 中创建 Motorcycle 的实例。

标签: ruby oop class-variables


【解决方案1】:

首先,与实例化无关;这是一个类变量,而不是实例变量。您的代码从不实例化任何东西;正如所写的那样,它也不需要。我们只是在谈论一些课程。但是类是 Ruby 中的一等对象,所以没关系。

其次,顺序很重要,因为在 Ruby所有语句都是可执行的。当你说

class Motorcycle < Vehicle
  @@wheels = 2
end

...所有这些代码现在执行,就在你说它的那一刻——当 Ruby 遵从指令走下页面时遇到它的那一刻。该代码不仅仅是未来某个时间要遵守的模板;它说:“创建一个摩托车类并将其类变量wheels 设置为2现在。”

第三,你没有“压倒”任何东西。在 Motorcycle 类中,@@wheels 与您之前为 Vehicle 定义的类变量 @@wheels 完全相同。 “覆盖”它的不是其他变量。这是一个单一值,其工作方式类似于一种命名空间全局,可从类、任何子类及其任何实例访问。

有关更多信息,请参阅(例如):

如果你想要可以覆盖的东西,你正在寻找的可能更像是类上下文中的实例变量,通常称为“类实例变量”——例如像这样:

class Vehicle
  @wheels = 4
  class << self
    attr_reader :wheels
  end
end

class Motorcycle < Vehicle
  @wheels = 2
end

puts Vehicle.wheels # 4
puts Motorcycle.wheels # 2

【讨论】:

  • 那么它是如何做到这一切的呢?
  • 什么是什么?
【解决方案2】:

所有出现的@@wheels 指的是完全相同的变量。首先将其设置为 4,然后将其设置为 2。为什么它的值是 2 让您感到惊讶?如果您想为两者使用单独的变量,请改用 @wheels(如 matt 建议)。

话虽如此(这是我写这个答案的唯一原因,因为马特已经在他的答案中涵盖了所有其他内容),你对这个“变量”的使用表明它应该是一个不变的属性相应的班级。也就是说,每辆摩托车都应该有两个轮子。在这种情况下,将其设为其类的常量可能更有意义:

class Motorcycle < Vehicle
  WHEELS = 2
end

class Unicycle < Vehicle
  WHEELS = 1
end

并将其用作

puts Motorcycle::WHEELS

如果你有一个变量v,你只知道它是Vehicle的一些子类,你可以写

puts v.class::WHEELS

您是否还在类 Vehicle 本身中定义 WHEELS,这是一个设计问题。如果您打算将Vehicle 视为抽象类,那么在其中定义固定数量的轮子是没有意义的。对于正交性,你可以通过做一个

来捕捉这种情况
class Vehicle
  WHEELS = nil
end

如果您愿意,或者只是不定义它并依赖于您的代码会引发异常的事实,如果您尝试访问“只是一辆普通车辆”的东西的轮子。

如果您确实想实例化 Vehicle,当然将其中的常量设置为 4 可能是有意义的。

但是,在这种情况下,我会考虑允许单个实例具有不同数量的轮子。 Reliant Robin 是一辆有 3 个轮子的汽车,而卡车有时有 6 个或 8 个轮子。因此,为了最大的通用性,我会将轮子的数量设为实例变量,当然您可以提供一个默认值:

class Vehicle
  attr_reader :wheels
  def initialize(wheels=4)
    @wheels=wheels
  end
end
class Motorcycle < Vehicle
  attr_reader :wheels
  def initialize(wheels=2)
    super(wheels)
  end
end

m = Motorcycle.new
puts m.wheels

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-27
    相关资源
    最近更新 更多