Ruby Ruby 的变量

kevinclcn · 2015年08月08日 · 最后由 rubyyjc520 回复于 2019年01月03日 · 4895 次阅读

Global variable

全局变量以$开头,它是全局可见的。Ruby 内建的全局变量如$:$LOAD_PATH表示 require 读取文件时寻找的目录数组。

Class variable

类变量以@@开头,可被定义它的类以及其子类访问,也可被定义它的类和子类的实例访问。

class Example
  @@cls_var = "defined in Example"

  def self.cls_var
    @@cls_var
  end

  def inst_var
      @@cls_var
  end
end

puts Example.cls_var # => "defined in Example"
puts Example.new.inst_var # => "defined in Example"

class SubExample < Example
  @@cls_var = "modified in SubExample"
end

puts SubExample.cls_var # => "modified in SubExample"
puts SubExample.new.inst_var # => "modified in SubExample"

class variable 并不真正属于类,而是属于类体系结构。

@@cls_var = 1

class Example
  @@cls_var = 2
end

puts @@cls_var # => 2

因为@@cls_var被定义在 Object 类上,而 Example 继承自 Object,所以 Example 也共享了这个类变量。

class Example
  @@foo = ""
  def foo
    @@foo 
  end
end

class SubExample < Example
  @@foo = "hello"
end

puts Example.new.foo # => "hello

Example.new 并不属于 SubExample,竟然也被 SubExample 的修改所影响!

正因为很多地方都可以改变类变量,很难追踪是什么地方做了改变,所以不推荐使用它。

Instance variable

在类对象上定义的变量是实例变量,以@开头,实例变量因为存在于在对象上,所以整个对象的类的继承链都可以使用。这与 Java 等静态语言不一样。

父类里定义的实例变量,子类方法可以使用:

class Example
  def def_inst_var
    @inst_var = "I am defined in Example"
  end
end

class SubExample < Example
  def inst_var
    @inst_var
  end
end

sub = SubExample.new
sub.def_inst_var
puts sub.inst_var # => "I am defined in Example"

子类里定义的变量,父类方法也可以使用:

class Example
  def inst_var
    @inst_var
  end
end

class SubExample < Example
  def def_inst_var
    @inst_var = "I am defined by SubExample"
  end
end

sub = SubExample.new

sub.def_inst_var
puts sub.inst_var # => "I am defined by SubExample"

Class Instance variable

类本身也是一种对象,它是 Class 类的实例,在类上定义的变量,叫类实例变量。类实例变量只能被类方法访问。

class Example
  @cls_inst_var = "class instance variable"
  def self.cls_inst_var
    @cls_inst_var
  end
end

puts Example.cls_inst_var # => "class instance variable"

类实例变量因为存放在类对象上,所以能够被继承链上的类方法访问到。比如子类定义的类实例变量,父类的类方法也能访问:

class Example 
  def self.cls_inst_var
    @cls_inst_var
  end
end

class SubExample < Example
  @cls_inst_var = "class instance variable"
end

puts SubExample.cls_inst_var # => "class instance variable

Local variable

局部变量只属于当前的作用域,作用域的改变只取决于三个关键字:class, moduledef。跟 Java 等语言不同,for,if,while 等关键字并不会改变作用域。

def test(ok)
  if ok
    a = "I am OK!"
  end
  puts a
end

test(false) # => ""
test(true)  # => "I am OK!"

楼主打算写书吗

最近 team 在做一个 rails 项目,大家都是 rails 新手,就把知道终结一下,分享出来,顺便也贴到这边给大家看看

这个似乎与说明不符。

#3 楼 @chenge 多谢提醒,这个确实有问题,我来改正。类实例变量定义在类实例上,Example 和 SubExample 不是同一个实例对象,所以应该不能访问。

class Example
  @cls_inst_var = "class instance variable"
  def query 
      @cls_inst_var  
  end 
end

e=Example.new
puts e.query

这种写法必须写构造函数或者类实例变量同名方法才能访问

class V

    def save(var)
       @cls_inst_var=var
    end

    def query 
       @cls_inst_var
    end
end 

class VV < V

end 

v=VV.new

v.save(100)
puts v.query

这样写一样继承,还省去了构造方法/同名方法 更简单

#5 楼 @yakczh 你写的第一段代码中的变量是类实例变量,只能在类方法中访问到,query不是类方法。

@@ 相当于 public static

class Example 
  @cls_inst_var = "class instance variable"
  def self.cls_inst_var
    @cls_inst_var
  end
end

中的@ 相当于 private

class Example 
  def query
    @cls_var
  end  
  def save(var)
    @cls_var=var
  end
end

中的@相当于 protected

#2 楼 @kevinclcn 真好,有 team。。。

#7 楼 @yakczh 不能把 Ruby 的变量理解成 C++ 派系语言里的变量,也不能把 Ruby 里的 public,protected 和 private 理解成 C++ 里的 public,protected 和 private。Ruby 里的 public,protected 和 private 只用来修饰方法。Ruby 里变量的可见性由作用域控制。在当前作用域内只能看到属于自己作用域的局部变量,当前对象 self 上的实例变量,全局变量,当前类体系里的类变量。关于 Ruby 的作用域,可以看我的另一个帖子 https://ruby-china.org/topics/26824

你的这段代码我觉得是有歧义的,

class Example
  def inst_var
    @inst_var
  end
end

class SubExample < Example
  def def_inst_var
    @inst_var = "I am defined by SubExample"
  end
end

sub = SubExample.new

sub.def_inst_var
puts sub.inst_var # => "I am defined by SubExample"

你说子类里定义的的实例变量,父类方法也可以使用。会让人误解父类可以调用子类的实例变量。

但其实是,父类里定义的实例变量是属于父类的实例的,而子类里定义的实例变量是属于子类实例的。 这段代码为什么子类能调用父类的方法访问自己的实例变量,只是因为子类继承了这个方法罢了。

如果

ex = Example.new
ex.inst_var # => nil 

而上面的那段代码说“父类里定义的实例变量,子类方法可以使用”

class Example
  def def_inst_var
    @inst_var = "I am defined in Example"
  end
end

class SubExample < Example
  def inst_var
    @inst_var
  end
end

sub = SubExample.new
sub.def_inst_var
puts sub.inst_var # => "I am defined in Example"

也是有问题的,子类也只是调用从父类继承的方法,来设置自己的实例变量。

ex = Example.new
ex.instance_variables # => []

#10 楼 @aidewoode 我这么写主要是想从 c++ 或 java 语言的角度来比较。你代码里的 Example.new 本身不是一个 SubExample 实例,方法无法访问 SubExample 的实例变量也是正常的

@kevinclcn 主要是这样的话,可能会让一些初学者产生误解。我用 Example.new 也只是想说明父类和子类中的实例变量是没有关系的。

#12 楼 @aidewoode Ruby 初学者单靠这一篇文章确实不容易搞清楚。估计还需要一篇方法的查找和运行。这里主要是强调它与 Java 的区别,Java 中变量是有 public,protected,private 之分的,所以父类里定义的成员变量,子类的方法不能访问。所以我上面有提到”实例变量因为存在于在对象上,所以整个对象的类的继承链都可以使用。这与 Java 等静态语言不一样

@kevinclcn 好吧,期待你的下一篇文章,:plus1:

aidewoode 回复

我作为初学者就进入了你说的歧义中。看了你的表述才重新回到正途👍 👍 👍

需要 登录 后方可回复, 如果你还没有账号请 注册新账号