类方法正在访问实例变量

时间:2016-10-24 02:17:46

标签: ruby

我想了解这段代码。为什么它会返回Hello而不是Howdy!

class Speaker
  @message = "Hello!"

  class << self
    @message = "Howdy!"

    def speak
      @message
    end
  end
end

puts Speaker.speak

3 个答案:

答案 0 :(得分:4)

首先,您的消息@message不是实例变量,或者不是您可能正在考虑的实例变量的类型:它是类级实例var,因此实例变量为{{1} }本身,作为对象是类Speaker的实例。

这是一个代码版本,用于执行您尝试使用局部变量和闭包执行的操作:

Class

这里有一些代码说明了类级实例变量和“普通”实例变量之间的区别:

class Speaker
  @message = "Hello!"

  class << self
    message = "Howdy!"
    define_method(:speak) { message }
  end
end

Speaker.speak
#=> "Howdy!"

答案 1 :(得分:3)

这是你的代码,除了我以通常的方式(def self.speak...)定义了类方法。因为类方法只不过是在类中定义的实例方法。单例类,这种变化只是创建相同类方法的另一种方式。 (如果你对此表示怀疑,请在两种方式下运行代码。)我做了这个改变,因为我认为它会让我对正在发生的事情的解释更清楚。我还添加了puts声明。

class Speaker
  @message = "Hello!"

  def self.speak
    puts "self=#{self}"
    @message
  end

  class << self
    @message = "Howdy!"
  end
end

类定义的第一行创建一个类实例变量@message

Speaker.instance_variables
  #=> [:@message]
Speaker.instance_variable_get(:@message)
  #=> "Hello!"

通过对比,

@message = "Howdy!"

Speaker的单例类上创建一个实例变量:

Speaker.singleton_class.instance_variables
  #=> [:@message]
Speaker.singleton_class.instance_variable_get(:@message)
  #=> "Howdy!"

现在在speak上调用Speaker

Speaker.speak
  # self=Speaker
  #=> "Hello!" 

作为self #=> Speakerspeak显然是返回类实例变量的值。

要让speak返回Speaker的单例类中定义的实例变量的值,我们可以编写以下内容:

class Speaker
  @message = "Hello!"

  def self.speak
    puts "self=#{self}"
    puts "singleton_class = #{singleton_class}"
    singleton_class.instance_variable_get :@message
  end

  class << self
    @message = "Howdy!"
  end
end

puts Speaker.speak
  # self=Speaker
  # singleton_class = #<Class:Speaker>
  # Howdy!

在最后一个表达式中,因为self等于Speakerself是没有显式接收器的隐含接收器,"singleton_class等同于Speaker.singleton_class

答案 2 :(得分:-1)

此代码返回“Hello”的原因是它正在尝试更改类中的实例变量&lt;&lt;自我阻止。

类方法适用于任何不处理该类的单个实例的事物 - 实例变量与类的各个实例相关联,并且我们无法在类级别更改实例变量。

我们应该使用类变量(用@@表示),而不是在speak方法中使用实例变量。

例如,以下代码将返回'你好!' -

class Speaker
    @@message = "Hello!"

    class << self
        @@message = "Howdy!"

        def speak
           @@message
        end
    end
end

puts Speaker.speak