将依次搜索特殊方法、本类中定义的方法和超类(包括Mix-in进来的模块。写成 类名.ancestors。)中定义的方法,并执行所找到的第一个方法。若没有找到方法时,将按照同样的顺序来搜索method_missing。
module Indexed def [](n) to_a[n] end end class String include Indexed end p String.ancestors # [String, Indexed, Enumerable, Comparable,遗憾的是上述代码返回的是10,而并非预期的"b\n"。这是因为系统在String类中搜索[],在遇到Indexed中定义的方法之前就已经完成了匹配,所以如此。若直接在Class String中重定义[]的话,就会如您所愿了。
5.2 +和-是操作符吗?+和-等是方法调用,而并非操作符。因此可进行overload(重定义)。
class MyString < String def +(other) print super(other) end end但以下内容及其组合(!=、!~)则是控制结构,不能进行重定义。
=, .., ..., !, not, &&, and, |, or, ~, ::重定义(或者定义)操作符时,应该使用形如+@或-@这样的方法名。
=是访问实例变量的方法,您可以在类定义中使用它来定义方法。另外,+或-等经过适当的定义之后,也可以进行形如+=这样的自赋值运算。
def attribute=(val) @attribute = val end 5.3 Ruby中有函数吗?Ruby中看似函数的部分实际上都是些省略被调(self)的方法而已。例如
def writeln(str) print(str, "\n") end writeln("Hello, World!")中看似函数的部分实际上是Object类中定义的方法,它会被发送到隐藏的被调self中。因此可以说Ruby是纯粹的面向对象语言。
对内部函数这种方法来说,不管self如何,它们总是返回相同的结果。因此没有必要计较被调的问题,可以将其看作函数。
5.4 可以在外部使用对象的实例变量吗?不能直接使用。若想操作实例变量,必须事先在对象中定义操作实例变量的方法(aclearcase/" target="_blank" >ccessor)。例如
class C def name @name end def name=(str) # name 后面不能有空格! @name = str end end c = C.new c.name = '山田太郎' p c.name #=> "山田太郎"另外,您还可以使用Module#attr、attr_reader、 attr_writer、attr_accessor等来完成这种简单的方法定义。例如,您可以这样来重写上面的类定义。
class C attr_accessor :name end若您不愿定义访问方法,却想使用实例变量时,可以使用Object#instance_eval。
5.5 private和protected有什么不同?private意味着只能使用函数形式来调用该方法,而不能使用被调形式。所以,您只能在本类或其子类中调用private方法。
protected也是一样,只能用在本类及其子类中。但是您既可以使用函数形式又可以使用被调形式来调用它。
在封装方法时,该功能是必不可少。
5.6 能不能将实例变量变成public类型的变量?无法让变量变成public类型的变量。在Ruby中访问实例变量时,需要使用访问方法。例如
class Foo def initialize(str) @name = str end def name return @name end end但是每次都这么写的话,未免有些繁琐。因此可以使用attr_reader、attr_writer、 attr_accessor等方法来完成这些简单的方法定义。
class Foo def initialize(str) @name = str end attr_reader :name # 其效果等同于下面的代码。 # def name # return @name # end end foo = Foo.new("Tom") print foo.name, "\n" # Tom您还可以使用attr_accessor来同时定义写入的方法。
class Foo def initialize(str) @name = str end attr_accessor :name # 其效果等同于下面的代码。 # def name # return @name # end # def name=(str) # @name = str # end end foo = Foo.new("Tom") foo.name = "Jim" print foo.name, "\n" # Jim若只想定义写入方法的话,可以使用attr_writer。
5.7 怎样指定方法的可见性?首先 Ruby把那些只能以函数形式(省略被调的形式)来调用的方法叫做private方法。请注意,这里的private定义与C++以及Java中的定义不同。
若将方法设为private类型之后,就不能在其它的对象中调用该方法了。因此,若您只想在本类或其子类中调用某方法时, 就可以把它设为private类型。
您可以这样把方法设为private类型。
class Foo def test print "hello\n" end private :test end foo = Foo.new foo.test #=> test.rb:9: private method `test' called for #<Foo:0x400f3eec>您可以使用private_class_method将类方法变为private类型。
class Foo def Foo.test print "hello\n" end private_class_method :test end Foo.test #=> test.rb:8: private method `test' called for Foo(Class)同理,您可以使用public、public_class_method将方法设为public类型。
在默认情况下,类中的方法都被定义成public类型(initialize除外),而顶层中的方法会被定义成private类型。
5.8 方法名可以用大写字母开头吗?可以。但要注意:即使方法调用中不带参数,也不能省略方法名后的空括号。
5.9 为什么使用super时会出现ArgumentError?在方法定义中调用super时,会把所有参数都传给上层方法,若参数个数不符合其要求,就会引发ArgumentError。因此,若参数个数不合时,应该自己指定参数然后再调用super。
5.10 如何调用上2层的同名方法?super只能调用上1层的同名方法。若想调用2层以上的同名方法时,需要事先对该上层方法进行alias操作。
5.11 重定义内部函数时,如何调用原来的函数?可以在方法定义中使用super。进行重定义之前,使用alias就可以保住原来的定义。也可以把它当作Kernel的特殊方法来进行调用。
5.12 何谓破环性的方法?就是能修改对象内容的方法,常见于字符串、数组或哈希表中。一般是这样的:存在两个同名的方法,一个会拷贝原对象并返回副本;一个会直接修改原对象的内容,并返回修改后的对象。通常后者的方法名后面带有!,它就是破坏性的方法。但是有些不带!的方法也是具有破环性的,如String#concat等等。
5.13 那些情况下会产生副作用?若在方法中对实参对象使用了破环性的方法的时候,就会产生副作用。
def foo(str) str.sub!(/foo/, "baz") end obj = "foo" foo(obj) print obj #=> "baz"此时,参数对象的内容被修改。另一方面,如果在程序中确有必要的话,也会对某对象发送具有副作用的消息,那就另当别论了。
5.14 能让方法返回多个值吗?在Ruby中确实只能指定一个方法返回值,但若使用数组的话,就可以返回多个值了。
return 1, 2, 3上例中,传给return的列表会被当作数组处理。这与下面的代码可谓是异曲同工。
return [1, 2, 3]另外,若使用多重赋值的话,则可以达到返回多个值的效果。例如
def foo return 20, 4, 17 end a, b, c = foo print "a:", a, "\n" #=> a:20 print "b:", b, "\n" #=> b:4 print "c:", c, "\n" #=> c:17您也可以这样处理。