在 Ruby 中,类和其他代码(对象、函数等)没有本质的区别。可以在类的定义中放任何代码:
3.times do
class C
puts "hel"
end
end
#输出
#hello
#hello
#hello
Ruby 会在类中运行任何可以运行的代码。但是需要重点的说明的是,上面的代码并不意味着定义了三个 C 类,比如下面的代码:
class D
def x; 'x'; end
end
class D
def y; 'y'; end
end
obj = D.new
obj.x # => "x"
obj.y # => "y"
从代码可以知道,上面的两个类 D 包含了两个地方定义的方法。也就是说,在第二次定义 D 类时,Ruby 能够找到第一次定义的 D 类并把新的方法(y)添加到 D 类中。在 Ruby 术语中,这叫做“打开类”(Open class)。
换句话说,可以在任何时候打开一个已经存在的类,并向里面添加新的方法——包括 String 和 Array 这样的标准类。
【注意】 Ruby 的这种特性是完全不同于其他常见语言,比如 Java、Python 等。可能未接触过 Ruby 的人会觉得这样太乱了,比如可能(而且很有可能,尤其当不同人做开发时)在不同的时间在同一个类中引入同样的一个方法,那么就容易引起混乱(一般称这种在类中全局引入方法的方式叫做猴子补丁 [Monkeypatch])。但是,还是要补充一句,对于有自制力并且时刻记得“能力越大,责任越大”的人来说,Ruby 的这种特性能带来很牛 x 的体验。
Ruby 中的对象也是一个比较有意思的东西,与 Java、Python 等也具有很大的不同。比如下面的代码:
class MyClass
def my_method
@v = 1
end
end
obj = MyClass.new
obj.class # => MyClass
obj.instance_variables # => []
obj.my_method
obj.instance_variables # => [:@v]
在 Ruby 中,对象中存在实例变量 (instance variables) ,可以通过方法 instance_variables 进行查看当前对象中的 实例变量。
不过这里有意思的一点是,Ruby 中的实例变量和 类 是完全不搭嘎的。也就是说,Ruby 中对象中有哪些实例变量并不受类定义的影响。比如上面代码,在调用 obj.my_method前后,obj 中的实例变量是不一样的。或者可以这么认为:Ruby 中同一个类的不同实例对象可以具有不同的实例变量。(对于 javaer 或者 pythoner 来说,简直又一个喜闻乐见的特性)
Ruby 中的方法和其他语言中的方法类似,对于每个实例化对象,它只保存自己的实例变量,而方法都保存在对应的类中。
obj(OBJECT) | MyClass (CLASS) | |
---|---|---|
@v = 1 | (class) ——> | my_method() |
(instance variables) | (Methods) |
这里需要注意的是,Ruby 中的方法同样分 类方法和实例方法,其中实例方法只能对象被调用,而类方法则只能被类调用。
总结一下就是,类似于 Java 等语言,对象的实例变量在对象本地存在,而对象的方法在对象所对应的类中存在。但是和 Java 等编程语言不同的是,Ruby 中的类其实也是一种对象。那么 Ruby 基于这个前提又能引起哪些不可思议的特性呢,后面会一一道来。
是的, Ruby 中的类其实是一种对象。
因为类也是一种对象,因此所有对象的特性也是类具有的特性。比如,(Class) 有自己的类,这个类就是——Class,举个例子,假如一个字符串可以通过 str#class 获得它的类 String,那么 String 作为一个对象也可以通过 String#class 获知它自己的类。比如下面的代码:
"hello".class # => String
String.class # => Class
Class.class # => Class
这是一个完全不同于 Java 等开发语言的特性。可以这么认为,在 Java 中,类的实例对象只是一个可读的类,运行时可以随意生成某类的对象,却不能随意修改类的结构;但是在 Ruby 中,类的限制规范更少,可以像对象那样在运行时定义动态的类(通过 Class.new )。或者更简单地说:在运行时,其他语言只允许读类的信息,但是 Ruby 还允许编辑类的信息。