首页 > 代码库 > 《ruby编程语言》笔记2 对象

《ruby编程语言》笔记2 对象

ruby是一门非常纯粹的面向对象的语言:所有值都是对象,而且没有基本类型(primitive type)和对象类型的区别,这一点不同于其他语言。在Ruby中,所有对象都继承一个Object类,而且共享那些定义于此类中的方法。

对象引用object references

当我们在ruby中使用对象时,其实是在操作对象的一个引用,而非对象本身。当我们将一个值赋值给一个变量时,我们并没有将一个对象copy到该变量,而是在此变量中存储了一个指向那个对象的引用。下面代码可以说明这点:

s = "Ruby" # Create a String object. Store a reference to it in s.
t = s # Copy the reference to t. s and t both refer to the same object.
t[-1] = "" # Modify the object through the reference in t.
print s # Access the modified object through s. Prints "Rub".
t = "Java" # t now refers to a different object.
print s,t # Prints "RubJava".


When you pass an object to a method in Ruby, it is an object reference that is passed
to the method. It is not the object itself, and it is not a reference to the reference to the
object. Another way to say this is that method arguments are passed by value rather
than by reference, but that the values passed are object references.
Because object references are passed to methods, methods can use those references to
modify the underlying object. These modifications are then visible when the method
returns.

在ruby中,当把一个对象传递给一个方法时,其实传递的是该对象的引用,被传递的既不是对象本身,也不是一个指向该对象的引用的引用。换言之,方法实参时通过”值“而不是”引用“来传递的,只不过这里被传递的值正好 是对象的引用。(这里十分值得注意)

因为对象引用被传递给方法,所以方法可以利用这些引用来修改底层的对象。

Fixnum和symbol实现细节上不是引用,但可以看成”引用“。

Object Class and Object Type对象类和对象类型

There are several ways to determine the class of an object in Ruby. The simplest is
simply to ask for it:
o = "test" # This is a value
o.class # Returns an object representing the String class
If you are interested in the class hierarchy of an object, you can ask any class what its
superclass is:
o.class # String: o is a String object
o.class.superclass # Object: superclass of String is Object
o.class.superclass.superclass # nil: Object has no superclass
In Ruby 1.9, Object is no longer the true root of the class hierarchy:

在ruby1.9中,Object不再是所有类的根类了:

# Ruby 1.9 only
Object.superclass # BasicObject: Object has a superclass in 1.9
BasicObject.superclass # nil: BasicObject has no superclass

 

检查一个对象所属类的直接方法就是通过直接比较:
o.class==String

instance_of?方法同样可以完成同样的事情,但是更优雅:
o.instance_of? String

Usually when we test the class of an object, we would also like to know if the object is
an instance of any subclass of that class. To test this, use the is_a? method, or its
synonym kind_of?:

通常情况下,我们想要知道对象是类的某个子类的实例,使用is_a?或同义方法kind_of?
x = 1 # This is the value we‘re working with
x.instance_of? Fixnum # true: is an instance of Fixnum
x.instance_of? Numeric # false: instance_of? doesn‘t check inheritance
x.is_a? Fixnum # true: x is a Fixnum
x.is_a? Integer # true: x is an Integer
x.is_a? Numeric # true: x is a Numeric
x.is_a? Comparable # true: works with mixin modules, too
x.is_a? Object # true for any value of x

 

Class类定义了===操作符,可以替换is_a?
The Class class defines the === operator in such a way that it can be used in place of
is_a?:
Numeric === x # true: x is_a Numeric

This idiom is unique to Ruby and is probably less readable than using the more
traditional is_a? method.

 

在使用ruby编程的时候,我们常常不太在意一个对象所属的类是什么,只想知道是否可以在它身上调用某些 方法。让我们来看看<<这个例子。一般表示追加,如果我们正在编写一个产生文本输出的方法,那么也可以在该方法中使用这个操作符,然后在调用这个方法的时候,使用任何实现了<<操作符的参数。我们并不在意所属的类型,只要能够通过它调用<<操作符即可。我们可以使用respond_to?方法来测试这一点:

o.respond_to? :"<<"

这种做法的缺点在于它仅仅检查了一个方法的名称,而没有检查该方法的参数。比如,Fixnum和Bignum将<<实现为左移位操作符,而且希望其参数为一个数字而不是字符串。当我们使用respond_to?方法来测试时,整数对象看起来是”可追加的“,但是当我们试图在一个字符串后面添加一个整数时,就会产生错误。对于这个问题没有普遍试用的解决办法,但是对于本例中的情况,一个权宜之计是采用is_a?方法显式地将Numeric对象排除在外。


o.respond_to? :"<<" and not o.is_a? Numeric

关于类型和类之间的差别的另一个例子是StringIO类(ruby标准库)。StringIO使得我们可以对字符串对象进行读写。就像他们是IO对象一样,stirngIO魔法了IO的API。

关注类型而不是类,形成了Ruby中一种被称为” duck typing“的编程风格

对象的相等性Object equality

    ruby拥有多的出奇的方法来比较对象的相等性。了解他们的工作机制是非常重要的。这样才能在适当的时候选择正确的方法。

equal?方法

The equal? method is defined by Object to test whether two values refer to exactly the
same object. For any two distinct objects, this method always returns false:
a = "Ruby" # One reference to one String object
b = c = "Ruby" # Two references to another String object
a.equal?(b) # false: a and b are different objects
b.equal?(c) # true: b and c refer to the same object
By convention, subclasses never override the equal? method.
Another way to determine if two objects are, in fact, the same object is to check their
object_id:
a.object_id == b.object_id # Works like a.equal?(b)

 

==操作符

在Object类里,他是equal?方法的同义词。用于比较两个对象的引用是否是同样的。大多数类都重定义了这个操作符,使不同的实例之间也可以进行相等性测试

a = "Ruby" # One String object
b = "Ruby" # A different String object with the same content

a.equal?(b) # false: a and b do not refer to the same object
a == b # true: but these two distinct objects have equal values
针对java程序员的说明:

If you are a Java programmer, you are used to using the == operator to test if two objects
are the same object, and you are used to using the equals method to test whether two
distinct objects have the same value. Ruby’s convention is just about the opposite of
Java’s.

Numeric类在它们的==操作符里将进行简单的类型转换,因此(比如)fixnum 1和float 1.0通过==比较的结果是相等的。

irb(main):113:0> 1==1.0
=> true

 

诸如String和Array等类的==操作符,通常要求他的两个操作符属于同一个类。如果右操作数定义了一个to_str或to_ary转换方法,那么原先的==操作符就会调用右侧操作数定义的操作符==,以此来判定是否相等。

!=(不等于)在ruby中用于测试不等性。当ruby见到!=时,他会简单的使用==操作符并且反转结果。这意味着一个类只需定义==操作符来实现其相等性的含义。ruby会为你免费提供!=。但是ruby1.9中,类也可以显示定义他们自己的操作符。

 

eql?方法

Object将eql?方法定义成equal?方法的同义词,那些重定义了eql?方法的类通常将其作为一个严格版的==操作符,即不予许进行类型转换,比如:
1==1.0 # true:fixnum and float objects an be ==
1.eql?(1.0) #false: but they are never eql?

 

===操作符

===通常称为”条件相等性(case equality)"操作符,用于测试一个case语句的目标值是否和某个when从句相匹配。

Object类定义了一个默认的===操作符,他会调用==操作符,因此,对于许多类来说,条件相等性和==相等性是一样的。但是某些关键的类重新定义了===,使其不仅仅是一个成员关系或匹配操作符:
Range类定义了===,用于测试一个值是否位于某个范围内

Regexp定义了===,用于测试一个字符串是否与某个正则匹配

Class类定义了===,用于测试一个对象是否是该类的一个实例

在ruby1.9中,Symbol类定义了===,当其左右2个操作数是同一个符号对象时,或者当其右侧操作数是一个持有和左侧符号对象相同文本的字符串时,该操作符返回true。举例如下:

(1..10) === 5 # true: 5 is in the range 1..10
/\d+/ === "123" # true: the string matches the regular expression
String === "s" # true: "s" is an instance of the class String
:s === "s" # true in Ruby 1.9

像这样显式地使用===操作符并不多见,更为常见的是在一个case语句里隐式地使用它。

 

对象的顺序object orders 

 

在ruby中,类通过实现<=>操作符定义其顺序性。左边小于右边,返回-1,反之返回1.相等返回0。如果2个操作数之间的比较操作没有意义(如:假设右侧操作数属于另一个类)。那么该操作符返回nil。

 

1 <=> 5 # -1
5 <=> 5 # 0
9 <=> 5 # 1
"1" <=> 5 # nil: integers and strings are not comparable

只要定义了<=>操作符就可以对值进行比较,但是这并不直观,所以,典型情况下,那些定义了<=>操作符的类还会将Comparable模块作为一个mixin包含进来。

 

对象的转换 object conversion

许多ruby类定义了一些方法,他们可以返回对象的一种表示形式(representation),而这种形式本身却是另一个类的值。比如用于获取一个对象的Stirng表示形式的to_s方法。也许是最常被实现和最有名的了。

Explicit conversions 显示转换
Classes define explicit conversion methods for use by application code that needs to
convert a value to another representation. The most common methods in this category
are to_s, to_i, to_f, and to_a to convert to String, Integer, Float, and Array,
respectively.
Built-in methods do not typically invoke these methods for you. If you invoke a method
that expects a String and pass an object of some other kind, that method is not expected
to convert the argument with to_s. (Values interpolated into double-quoted strings,
however, are automatically converted with to_s.)

建的方法通常不会自动调用。如果你调用一个参数为String的方法,却传递一个其他类型的对象给它,该方法是不会主动调用参数的to_s方法来进行转换的。(然后,对于那些内插在双引号里面的字符串的值,ruby会利用to_s来自动转换)

 

(我能说ruby是强类型的吗?)

 

3.8.7.2 Implicit conversions 隐式转换
Sometimes a class has strong characteristics of some other class. The Ruby Exception
class represents an error or unexpected condition in a program and encapsulates an
error message. In Ruby 1.8, Exception objects are not merely convertible to strings; they
are string-like objects and can be treated as if they were strings in many contexts.* For
example:
# Ruby 1.8 only (1.9运行不了)
e = Exception.new("not really an exception")
msg = "Error: " + e # String concatenation with an Exception
Because Exception objects are string-like, they can be used with the string concatenation
operator. This does not work with most other Ruby classes. The reason that
Exception objects can behave like String objects is that, in Ruby 1.8, Exception implements
the implicit conversion method to_str, and the + operator defined by String
invokes this method on its righthand operand.
Other implicit conversion methods are to_int for objects that want to be integer-like,
to_ary for objects that want to be array-like, and to_hash for objects that want to be
hash-like. Unfortunately, the circumstances under which these implicit conversion
methods are called are not well documented. Among the built-in classes, these implicit
conversion methods are not commonly implemented, either.

对于在何种情形下这些隐士转换方法会被调用,ruby没有很好的文档化。此外,在内建类里,这些隐士转换方法也没用被普遍地实现。

 

转换函数

Kernel模块定义了4个转换方法,作为全局转换函数。这些函数--Array、float、Integer即String。具有和他们将要转换的类相同的名字,

Array函数试图调用to_ary方法来将其实参转换为一个数组。如果没有定义to_ary方法,或者该方法返回nil,那么Array函数就会试着调用to_a方法。如果没有定义to_a方法,或者该方法返回nil,那么Array函数就会简单地返回一个新数组,并且将其实参作为该数组的一个元素。