Ruby Object 类详细分析(1)

RubyObject是所有类的父对象,因此它的所有方法在所有对象中都是可用的(如果没有被覆盖的话)。因此它的重要性那是不言而喻的。

Object到底实现哪些功能,下面一一道来:

1.       对象比较

Ruby定义了若干对象比较的方法,有=====eql?equal?,是不是很头大,不过还没完,还有一个=~ 。下面我们对这些比较方式做一个说明。

首先是==,只有两个对象相同时,该方法的返回值为true。但是子类一般都会对这个方法进行过载,来表示语义上的相等。比如字符串对象和数值对象都对==方法进行了过载。与之相对照的是,equal?方法不应该被子类过载,只有在两个对象表示同一个对象的情况下才会返回true值(注意,是不应该,而不是不能。没有人可以阻止你在Ruby的世界里干这样的坏事,不过你愤怒的老板会给你一张黄牌)。

首先看一个简单的例子:

class MyClass
   attr_reader :str
   def initialize(s)
         @str = s
   end
end

obj1 = MyClass.new("hello")
obj2 = MyClass.new("hello")

puts obj1 == obj2          #false
puts obj1.equal?(obj2)     #false

可以看出,在一般情况下,==equal?的行为是一样的,不过我们通常用比较有意义的方式来过载==方法:

class MyClass
   attr_reader :str
   def initialize(s)
         @str = s
   end
  
   def ==(o)
         str == o.str
   end
  
#
你不应该这样做!
#  def equal?(o)
#        str == o.str
#  end
end

obj1 = MyClass.new("hello")
obj2 = MyClass.new("hello")

puts obj1 == obj2          #true
puts obj1.equal?(obj2)     #false

可以看出,我们重新定义的==方法使得比较成功了,再仔细看看,我们是使用了String对象的==方法来实现我们自己定义的==方法的,你应该看得出来,String类已经重载了==方法(否则我们的相等操作无法返回true),如果还没有看出来,下面的这段代码可以帮助你:

s1 = "hello"
s2 = "hello"

puts s1 == s2              #true
puts s1.equal?(s2)         #false

s3 = s1

puts s1 == s3              #true
puts s1.equal?(s3)         #true

应该很清楚了,在定义自己的新类的时候,你通常应该为它定义一个比较有意义的==方法,但是你不应该为它重新定义一个equal?方法。

那么,eql?的作用是什么?如果两个对象的值相等的话,那么eql?方法的返回值为trueeql?方法有一个特殊的作用,它被Hash对象用来测试键值是否相等(注意,还需要有hash方法来配合使用,这个方法在后面介绍)。

class MyClass
  attr_reader :str
  def initialize(s)
    @str = s
  end
 
  def eql?(o)
    str.eql?(o.str)
  end
 
  def hash
    str.hash
  end
end

obj1 = MyClass.new("hello")
obj2 = MyClass.new("hello")

puts obj1 == obj2       #false
puts obj1.eql?(obj2)    #true

hash = {}

hash[obj1] = 1
puts hash[obj2]         #1

hash[obj2] = 2
puts hash[obj1]         #2

Object类中,==eql?的行为是一致的,一般说来,在定义具体类时,也应该保持这样的方式。不过在系统定义的类中有一些特例,比如在Numeric类型的类中,==要在类型和值都相同时才会返回真值,而eql?方法则只要值相等就可以:

   1 == 1.0     #=> true

   1.eql? 1.0   #=> false

 

===Ruby的文档中称作“case equality”,它的主要用途是在case语句中,用来判断case语句中when子句的匹配状况。当然你也可以直接在语句中使用它,比如判断一个对象是否在一个范围内就可以使用===来判断:

puts (1..10) === 2         #=>true
puts (1..10) === 11        #=>false

值得注意的是是Range类重载了===方法,因此Range对象实例必须在左侧,下面的结果你应该不会感到意外:

puts 2 === (1..10)         #=>false
puts (1..10) === 11        #=>false

下面我们用一个故意设计的类(其实可以直接用Range的)来演示一下在自己定义的类中过载===方法,我们定义一个ScoreRange的类来对分数进行判断:

class ScoreRange
   def initialize(min, max)
         @min = min
         @max = max
   end
  
   def ===(score)
         (@min..@max) === score
   end
end

PASSED = ScoreRange.new(60, 100)
FAILED = ScoreRange.new(0, 59)
GOOD = ScoreRange.new(80, 100)

score = 68

case score
   when GOOD
         puts "Good!"
   when PASSED
         puts "Passed!"
   when FAILED
         puts "Failed!"
end

最后的输出结果回事“Passed!”,当然,这个程序还存在瑕疵,比如对59.5这样的成绩没有理会,也没有无效分数的判断,它只是用来演示===方法的过载。

最后一个=~方法是用于正则表达式的模式匹配的,Object的子类应该用比较有意义的方式来过载它,在Object类中,该方法总是返回false。它的具体使用方式可以参见正则表达式的部分,这里不做详细剖析。

 

你可能感兴趣的:(Ruby Object 类详细分析(1))