深入探索
子类中的 protected 和 private
调用父类和子类的对象上的方法时,适用相同的访问规则。也就是说,当你传递给一个方法一个对象(作为一个参数),该对象与接收者对象(即方法所属的对象)具有相同的类,参数对象可以调用类的 public 和 protected 方法,但不能调用其 private 方法。
有关此示例,请查看 protected.rb 程序。在这里,我创建了一个名为 myob
的 MyClass 对象和一个 MyOtherClass 对象 myotherob
,并且 MyOtherClass 继承自 MyClass。我尝试将 myotherob
作为参数传递给 myob
的公共方法,shout
:
myob.shout( myotherob )
但是 shout
方法在参数对象上调用 private 方法 priv
:
def shout( anOb ) # calls a private method
puts( anOb.priv( "This is a #{anOb.class} - hurrah" ) )
end
这将不能运行!Ruby 解释 priv
方法是私有的。
同样,如果我反过来这样做 - 也就是说,通过将父类对象 myob
作为参数传递,并在子类对象上调用方法 shout
,我会得到同样的错误:
myotherob.shout( myob )
MyClass 类还有另一个公共方法,exclaim
。这次会调用一个 protected 方法,prot
:
def exclaim( anOb ) # calls a protected method
puts( anOb.prot( "This is a #{anOb.class} - hurrah" ) )
end
现在,我可以将 MyClass 对象 myob
或 MyOtherClass 对象 myotherob
作为参数传递给 exclaim
方法,并且在调用 protected 方法时都不会发生错误:
myob.exclaim( myotherob ) # This is OK
myotherob.exclaim( myob ) # And so is this…
不用说,这仅在两个对象(接收器和参数)共享相同的继承链时才有效。如果发送不相关的对象作为参数,则无论其保护级别如何,你都无法调用接收器对象所属类的方法。
突破 private 方法的隐私限制
私有方法的重点在于它不能从它所属的对象作用域之外被调用。所以这将不起作用:
class X
private
def priv( aStr )
puts("I'm private, " << aStr)
end
end
ob = X.new
ob.priv( "hello" ) # This fails
然而,事实证明 Ruby 以一种叫做 send
方法的形式提供了一个’get out’子句(或者我应该说’get in’子句?)。send
方法调用方法名称与符号(一个以冒号开头的标识符,例如 :priv
)相匹配的方法,该方法名称作为第一个参数传递给 send
,如下所示:
ob.send( :priv, "hello" ) # This succeeds
符号后面提供的任何参数(如字符串,”hello”)都以正常方式传递给指定的方法。
可以说使用 send
获取私有方法的公共访问权通常不是一个好主意(否则,为什么你首先将该方法设为私有的),所以应谨慎使用或根本不使用…
单例类方法
之前,我们通过将方法名称附加到类的名称后面来创建类方法(class method),如下所示:
def MyClass.classMethod
有一种“快捷”语法。这是个示例:
class MyClass
def MyClass.methodA
puts("a")
end
class << self
def methodB
puts("b")
end
def methodC
puts("c")
end
end
end
这里,methodA
,methodB
和 methodC
都是 MyClass 的类方法;methodA
是使用我们之前使用的语法声明的:
def <ClassName>.<methodname>
但是,使用实例方法的语法声明了 methodB
和 methodC
:
def <methodname>
那么为什么它们最终成为类方法呢?这完全归结于方法声明已放在此代码中:
class << self
# some method declarations
end
这可能会让你想起用于声明单例类(singleton classe)的语法。例如,在 singleton_class.rb 程序中,你可能还记得我们首先创建了一个名为 ob
的对象,然后给它声明了一个自己的方法,blather
:
class << ob
def blather( aStr )
puts("blather, blather #{aStr}")
end
end
这里的 blather
方法是 ob
对象的单例方法(singleton method)。类似地,在 class_methods3.rb 程序中,methodB
和 methodC
方法是 self
的单例方法,而 self
恰好是 MyClass 类。我们可以类似地通过使用 <<
后跟类名来从类定义之外添加单例方法,如下所示:
class << MyClass
def methodD
puts( "d" )
end
end
嵌套方法
你可以嵌套(nest)方法(将一个方法嵌套在另一个方法中)。这为你提供了一种将长方法划分为可重用块的方式。因此,例如,如果方法 x
需要在几个不同的点进行 y
计算,则可以将 y
方法放在 x
方法中:
class X
def x
print( "x:" )
def y
print("ha! ")
end
def z
print( "z:" )
y
end
y
z
end
end
嵌套方法默认在定义它们的作用域之外是不可见的。因此,在上面的示例中,虽然可以从 x
内部调用 y
和 z
,但是任何其它代码都不能调用它们:
ob = X.new
ob.y #<= error
ob.z # <= error
但是,当你运行一个包含嵌套方法的方法时,这些嵌套方法将被带入该方法之外的作用域内!
class X
def x
print( "x:" )
def y
print("y:")
end
def z
print( "z:" )
y
end
end
end
ob = X.new
ob.x #=> x:
puts
ob.y #=> y:
puts
ob.z #=> z:y:
方法名称
最后一点,值得一提的是 Ruby 中的方法名称几乎总是以小写字符开头,如下所示:
def fred
但是,这只是一个习惯约定,而非必须的。也可以用大写字母开头的方法名称,如下所示:
def Fred
由于 Fred
方法看起来像一个常量(以大写字母开头),因此你需要在调用它时添加括号来告诉 Ruby,它是一个方法:
Fred # <= Ruby complains "uninitialized" constant
Fred() # <= Ruby calls the Fred method
总的来说,最好坚持使用以小写字符开头的方法名称的约定。