【问题标题】:Ruby class variablesRuby 类变量
【发布时间】:2011-01-06 06:28:27
【问题描述】:

ruby 类实例的东西让我头疼。我明白了……

class Foo
  @var = 'bar'
end

...@var 是已创建类的实例上的变量。

但是如何创建子类可覆盖的类变量呢?

这是我将在 Python 中执行的示例:

class Fish:
var = 'fish'
def v(self):
    return self.var

class Trout(Fish):
    var = 'trout'

class Salmon(Fish):
    var = 'salmon'

print Trout().v()
print Salmon().v()

哪些输出:

trout
salmon

我如何在 ruby​​ 中做同样的事情?

【问题讨论】:

  • 不应该是第一个代码块中的@@var 吗?
  • 我正在阅读 David Black 的“The Well-Grounded Rubyist” (manning.com/black2)。他很好地解释了该领域的所有细微差别
  • Jean:不。如果我使用@@var,那么子类将覆盖父类。请参阅 hobodave 的链接。特别是在示例中如何覆盖“2”。

标签: ruby class inheritance class-variables


【解决方案1】:

为了对比@khellll 的回答,这里使用 Class 对象上的实例变量:

class Fish
  # an instance variable of this Class object
  @var = 'fish'

  # the "getter"
  def self.v
    @var
  end

  # the "setter"
  def self.v=(a_fish)
    @var = a_fish
  end
end

class Trout < Fish
  self.v = 'trout'
end

class Salmon < Fish
  self.v = 'salmon'
end

p Trout.v   # => "trout"
p Salmon.v  # => "salmon"

编辑:赋予实例对类的实例变量的读取权限:

class Fish
  def type_of_fish
    self.class.v
  end
end

p Trout.new.type_of_fish   # => "trout"
p Salmon.new.type_of_fish  # => "salmon"

【讨论】:

  • 不,不完全是。最后你没有做 Trout.new 或 Salmon.new。您正在使用课程本身。我希望 instance 获取类变量。
  • @The Doctor -- 现在怎么样?
  • 是的。我认为它现在在功能上是等效的。对我给出的答案。您只是手动编写访问器。
  • 必须编写访问器:所有实例变量都是私有的,attr_* 方法适用于实例对象而不是类对象。
【解决方案2】:
上面提到的

@var叫做类实例变量,和实例变量是不一样的……阅读答案here看区别。 p>

无论如何这是等效的 Ruby 代码:

class Fish
  def initialize
    @var = 'fish'
  end

  def v
    @var
  end
end

class Trout < Fish
  def initialize
    @var = 'trout' 
  end
end

class Salmon < Fish
  def initialize
    @var = 'salmon' 
  end
end

puts Trout.new.v
puts Salmon.new.v

【讨论】:

  • 这不是真的,因为每次子类化时都必须重写初始化。这不是很有用。在我的示例中,在初始化中包含一些代码可能会更好。
【解决方案3】:

这是我最终使用 hobodave 的链接找到的版本:

class Fish
  class << self
    attr_accessor :var
  end

  @var = 'fish'
  def v
    self.class.var
  end
end

class Trout < Fish
  @var = 'trout'
end

class Salmon < Fish
  @var = 'salmon'
end

puts (Trout.new).v   # => trout
puts (Salmon.new).v  # => salmon

注意,子类化只需要添加一个@var -- 不需要覆盖初始化。

【讨论】:

    【解决方案4】:

    这也是 Java 编码人员使用 Ruby 时常犯的错误,也是我必须弄清楚的重大概念跳跃之一。起初看起来很奇怪,但这确实是 Ruby 更酷的方面之一——所有代码都是可执行的,包括类定义。

    因此,实例变量必须在方法中声明。它与如何评估“自我”有关。 'self' 是当前对象。解释器将首先在'self'中查找方法调用和变量引用:

    class Fish
        @var = "foo" # here 'self' == Fish, the constant which contains the class object  
        def foo
            # do foo
        end
    end
    
    fish = Fish.new
    fish.foo # here 'self' == fish, an instance of Fish
    

    在类定义中,'self' 被设置为正在定义的类对象,因此类定义中的任何引用都将引用该类对象,在本例中为 Fish。

    但是,当在 Fish 的实例上调用方法时,self 被设置为调用的接收者,即 Fish 的特定实例。所以在方法定义之外,self 是类对象。在方法内部,self 是接收者的实例。这就是为什么方法定义外的@var 更像Java 中的静态变量,而方法定义内的@var 是实例变量的原因。

    【讨论】:

    • 错字:fish.foo 应该是fish.var
    • 其实,没有。我没有定义“foo”,但重点是 Ruby 解释器会将“fish”视为调用的接收者,并将“self”设置为“fish”以解析引用。为了清楚起见,我添加了 foo 方法。调用“fish.var”会抛出 NoMethodError。
    【解决方案5】:

    有一个问题:你可以覆盖@var:
    Salmon.var = 'shark' 将覆盖@var,所以
    puts (Salmon.new).v # => 鲨鱼

    【讨论】:

      猜你喜欢
      • 2013-03-24
      • 1970-01-01
      • 2012-05-22
      • 2013-04-18
      • 2021-07-29
      • 2013-04-29
      • 2013-11-26
      • 2014-09-21
      • 1970-01-01
      相关资源
      最近更新 更多