1. Class Methods
调用一个方法时, 可以通过定义这个方法的类的对象来调用, 那么这个称为实例方法.
还可以定义类方法, 这种方法的调用需要通过类来调用, 不能通过这个类的对象来调用.
其实我们创建的Class也是一个对象, 这个后面会讲到, 所以方法都是由对象来调用的.
执行结果 :
以下是错误的 :
class MyClass
def MyClass.classMethod # 定义class method可以使用这种写法, 后面还会介绍其他写法.class method也称为这个class的singleton method.
puts( "This is a class method" )
end
def instanceMethod
puts( "This is an instance method" )
end
end
# 也可以把class method的定义写在class外面 :
# def MyClass.classMethod
# puts( "This is a class method" )
# end
mobj1 = MyClass.new()
MyClass.classMethod #调用class method
mobj1.instanceMethod #调用instance method
执行结果 :
This is a class method
This is an instance method
以下是错误的 :
MyClass.instanceMethod #=> Error! This is an 'undefined method'
ob.classMethod #=> Error! This is an 'undefined method'
既然class method不能通过这个class的对象来调用, 那么它通常用在什么场合呢?
1.1. 还记得创建对象的时候用到的new方法吗, 它就是class method. 因为对象还没创建, 当然是没有办法调用instance method的. 所以这是一种使用情况.
1.2. 用在某些特殊的类中, 不需要创建对象反而使用更合理, 例如File类.
执行结果 :
2. Class Variables
#!/opt/ruby/bin/ruby
fn = 'test.rb'
if File.exist?(fn) then
puts(File.expand_path(fn))
puts(File.basename(fn))
puts(File.dirname(fn))
puts(File.extname(fn))
puts(File.mtime(fn))
puts("#{File.size(fn)} bytes")
else
puts( "Can't find file!")
end
执行结果 :
/root/rubyscript/test.rb
test.rb
.
.rb
2012-03-06 12:10:44 +0800
265 bytes
2. Class Variables
前面的章节已经介绍了几种变量 : 全局变量, 本地变量, 实例变量, 类变量.
类变量以@@开头, 作用域是类以及它的对象. 需要声明的是, 类变量必须在创建类时被初始化(可以为nil).
因为所有的Class都是Class的对象, 所以类也可以有它的实例变量.
再来看一个例子, 可以更清晰的知道, 类的实例变量存储的值是在它本身的, 而不是它的对象. 它的对象的实例变量的值可以通过initialize方法来初始化.
class Myclass
@@c1 = nil # 初始化时可以给nil
# @@c1 # 已经讲过, 类变量使用前必须初始化, 所以注释掉的这行是有问题的.
@i1
def Myclass.get
puts(@@c1)
end
end
再来看一个例子, 可以更清晰的知道, 类的实例变量存储的值是在它本身的, 而不是它的对象. 它的对象的实例变量的值可以通过initialize方法来初始化.
class MyClass
@@classvar = 1000
@instvar = 1000
def MyClass.classMethod
if @instvar == nil then
@instvar = 10
else
@instvar += 10
end
if @@classvar == nil then
@@classvar = 10
else
@@classvar += 10
end
end
def instanceMethod
if @instvar == nil then
@instvar = 1
else
@instvar += 1
end
if @@classvar == nil then
@@classvar = 1
else
@@classvar += 1
end
end
def showVars
return "(instance method) @instvar = #{@instvar}, @@classvar = #{@@classvar}"
end
def MyClass.showVars
return "(class method) @instvar = #{@instvar}, @@classvar = #{@@classvar}"
end
end
以上代码执行完后这个MyClass就有了一个类变量和一个实例变量, 初始值都是1000.
其中类变量的作用域是它和它的对象. 而实例变量的作用域仅仅是它自己.
下面来操作一下这个类 :
执行结果 :
for i in 0..2 do
ob = MyClass.new
MyClass.classMethod
ob.instanceMethod
puts( MyClass.showVars )
puts( ob.showVars )
end
执行结果 :
(class method) @instvar = 1010, @@classvar = 1011
(instance method) @instvar = 1, @@classvar = 1011
(class method) @instvar = 1020, @@classvar = 1022
(instance method) @instvar = 1, @@classvar = 1022
(class method) @instvar = 1030, @@classvar = 1033
(instance method) @instvar = 1, @@classvar = 1033
从结果上印证了MyClass的实例变量@instvar是属于它自己的, 它的对象的@instvar没有初始值.
而类变量@@classvar是它和它的对象都可以访问的.
另外再来看一个例子 :
使用
instance_variable_get这个singleton方法获取实例变量的值.
执行结果 :
把# MyClass.classMethod这行的注释去掉, 重新执行 :
3. A Class is an Class Object
class MyClass
@@classvar = 1000
@instvar = 1000
def MyClass.classMethod
if @instvar == nil then
@instvar = 10
else
@instvar += 10
end
end
def instanceMethod
if @instvar == nil then
@instvar = 1
else
@instvar += 1
end
end
end
ob = MyClass.new
puts MyClass.instance_variable_get(:@instvar)
puts( '--------------' )
for i in 0..2 do
# MyClass.classMethod
ob.instanceMethod # 它改变的只是它的实例变量值
puts( "MyClass @instvar=#{MyClass.instance_variable_get(:@instvar)}")
puts( "ob @instvar= #{ob.instance_variable_get(:@instvar)}" )
end
执行结果 :
1000
--------------
MyClass @instvar=1000
ob @instvar= 1
MyClass @instvar=1000
ob @instvar= 2
MyClass @instvar=1000
ob @instvar= 3
把# MyClass.classMethod这行的注释去掉, 重新执行 :
1000 -------------- MyClass @instvar=1010 ob @instvar= 1 MyClass @instvar=1020 ob @instvar= 2 MyClass @instvar=1030 ob @instvar= 3
3. A Class is an Class Object
前面已经多次提到任何类都是Class类的对象.
require "Set"
require "Matrix"
class Myclass
end
p Myclass.class
p Array.class
p Set.class
p Vector.class
p Hash.class
p String.class
p Fixnum.class
p Float.class
输出全是Class.
4. Ruby Constructors : new or initialize ?
Constructor用于给Class构造一个对象, 在之前我们接触过类中定义的initialize方法 .
在创建一个对象时, 使用的是new方法, 而new方法会去查找这个类是否定义了initialize方法, 如果定义了initialize方法, 那么它会去调用initialize.
同时, 传给new的参数值将传递给initialize方法. initialize是一个private方法, 不能通过对象直接调用, 需要通过它的方法来调用. 这也就是通过new来调用initialize方法的来由了. 后面会讲到方法的级别.
例如 :
报错, 因为new方法调用initialize方法, 需要1个参数值传进去, 而在构造是没有传递进去. 所以报错了.
那么new方法能不能自定义呢, 最好不要这么做, 因为方法返回值是最后一行或显示的写明return. 来看一个例子 :
执行结果 :
那么把super放在new方法的最后一行, 再重试一下 :
执行结果 :
class Myclass
def initialize(a)
@x = a
end
end
mobj1 = Myclass.new()
报错, 因为new方法调用initialize方法, 需要1个参数值传进去, 而在构造是没有传递进去. 所以报错了.
C:/Users/digoal/Desktop/new.rb:2:in `initialize': wrong number of arguments (0 for 1) (ArgumentError)
from C:/Users/digoal/Desktop/new.rb:7:in `new'
from C:/Users/digoal/Desktop/new.rb:7:in `<main>'
mobj1 = Myclass.new(nil)
mobj1.initialize('digoal') # 这里报错, 原因是它去调用了private方法, private不允许直接通过对象调用, 只能通过方法调用
C:/Users/digoal/Desktop/new.rb:8:in `<main>': private method `initialize' called for #<Myclass:0x1dffdf0 @x=nil> (NoMethodError)
那么new方法能不能自定义呢, 最好不要这么做, 因为方法返回值是最后一行或显示的写明return. 来看一个例子 :
class MyClass
def initialize( aStr )
@avar = aStr
end
def MyClass.new( aStr )
super # 调用superclass的new方法.
@anewvar = aStr.swapcase # 这一行变成了new方法的返回值
end
end
ob = MyClass.new( "hello world" ) # 所以ob变成了String类的对象
puts( ob )
puts( ob.class )
执行结果 :
HELLO WORLD
String
那么把super放在new方法的最后一行, 再重试一下 :
class MyClass
def initialize( aStr )
@avar = aStr
end
def MyClass.new( aStr )
@anewvar = aStr.swapcase
super
end
end
ob = MyClass.new( "hello world" )
puts( ob )
puts( ob.class )
puts(ob.instance_variable_get(:@avar))
p(ob.instance_variable_get(:@anewvar))
执行结果 :
#<MyClass:0x1ee0e98>
MyClass
hello world
nil
显然在new方法中定义的@anewvar它的值并没有初始化到ob对象的@anewvar实例变量上.
那么它到哪里去了呢 ? 原来初始值在类里面 . 也就是说要初始值到类的对象里面, 我们必须在initialize中初始它.
5. Singleton Methods
puts(MyClass.instance_variable_get(:@anewvar))
执行结果 :
HELLO WORLD
5. Singleton Methods
Singleton method从字面上理解就是对象专有的方法, 例如有Class对象专有的方法, 自定义类对象的专有方法等.
以下例子使用了三种方法定义Myclass的singleton method.
使用了两种方法定义ob对象的singleton method
执行结果 :
我们来看看系统内置的类IO和File的singleton methods :
class Myclass
def initialize(a)
@x = a
end
def m1
puts("this is a Myclass's object's method.")
end
def Myclass.singleton_m1 # 第1种创建Myclass singleton method的方法.
puts("this is a Myclass's singleton method.")
end
class << self # 第2种创建Myclass singleton method的方法.匿名类
def singleton_m2
puts("this is another Myclass's singleton method.")
end
end
end
class << Myclass # 第3种创建Myclass singleton method的方法, 这种方法可以写在class定义的外面. 匿名类
def singleton_m3
puts("this is another Myclass's singleton method.")
end
end
ob = Myclass.new('digoal')
class << ob # 第1种创建ob对象的singleton method的方法, 匿名类
def ob_singleton_m1
puts("this is a ob's singleton method.")
end
end
def ob.ob_singleton_m2 # 第2种创建ob对象的singleton method的方法,
puts("this is another ob's singleton method.")
end
p Myclass.singleton_methods # 打印出属于Myclass的singleton methods
p ob.singleton_methods # 打印出属于ob对象的singleton methods
ob.m1
ob.ob_singleton_m1
ob.ob_singleton_m2
Myclass.singleton_m1
Myclass.singleton_m2
Myclass.singleton_m3
ob1 = Myclass.new('DIGOAL')
ob1.m1
# 如果去掉以下注释, 以下报错, 因为它们调用了别的对象的singleton method.
=begin
ob1.ob_singleton_m1
Myclass.ob_singleton_m1
ob.singleton_m1
=end
执行结果 :
[:singleton_m1, :singleton_m2, :singleton_m3]
[:ob_singleton_m1, :ob_singleton_m2]
this is a Myclass's object's method.
this is a ob's singleton method.
this is another ob's singleton method.
this is a Myclass's singleton method.
this is another Myclass's singleton method.
this is another Myclass's singleton method.
this is a Myclass's object's method.
我们来看看系统内置的类IO和File的singleton methods :
p File.singleton_methods.sort
p IO.singleton_methods.sort
[:absolute_path, :atime, :basename, :binread, :binwrite, :blockdev?, :chardev?, :chmod, :chown, :copy_stream, :ctime, :delete, :directory?, :dirname, :executable?, :executable_real?, :exist?, :exists?, :expand_path, :extname, :file?, :fnmatch, :fnmatch?, :for_fd, :foreach, :ftype, :grpowned?, :identical?, :join, :lchmod, :lchown, :link, :lstat, :mtime, :new, :open, :owned?, :path, :pipe, :pipe?, :popen, :read, :readable?, :readable_real?, :readlines, :readlink, :realdirpath, :realpath, :rename, :select, :setgid?, :setuid?, :size, :size?, :socket?, :split, :stat, :sticky?, :symlink, :symlink?, :sysopen, :truncate, :try_convert, :umask, :unlink, :utime, :world_readable?, :world_writable?, :writable?, :writable_real?, :write, :zero?]
[:binread, :binwrite, :copy_stream, :for_fd, :foreach, :new, :open, :pipe, :popen, :read, :readlines, :select, :sysopen, :try_convert, :write]
如何来判断一个对象是否有某个singleton method呢, 我们前面已经通过singleton_methods返回了一个Array类型的对象.
那么我们可以通过Array的方法来判断是否有某个元素, 这样来判断这个方法是否存在于你要找的对象中.
例如 :
或者直接使用respond_to?方法 :
p File.singleton_methods.include?(:new)
结果 :
true
或者直接使用respond_to?方法 :
p File.respond_to?(:new)
结果 :
true
6. Ancestor Class
我们前面使用superclass方法可以找到调用这个方法的对象的父类, 例如 :
执行结果 :
7. Singleton Classes
def showFamily( aClass )
if (aClass != nil) then
puts( "#{aClass} :: about to recurse with aClass.superclass = #{aClass.superclass.inspect}" )
showFamily( aClass.superclass )
end
end
showFamily(Array)
执行结果 :
Array :: about to recurse with aClass.superclass = Object
Object :: about to recurse with aClass.superclass = BasicObject
BasicObject :: about to recurse with aClass.superclass = nil
7. Singleton Classes
singleton method和singleton class其实是一个概念, 前者是类对象的特有方法, 后者是对象的特有方法.
前面已经举了几种创建方法 .
8. Overriding Methods
def Classname.methodname
class << self
def methodname
class << Classname
def methodname
def objectname.methodname
class << objectname
def methodname
8. Overriding Methods
复写方法, 也多次列举了.
例如to_s方法, 顶级的to_s方法输出的是类名, 对象ID, 对象中的实例变量和它的值.
Array类复写了来自superclass的to_s方法, 我们来看个例子 :
inspect方法会去调用to_s方法, 所以我们复写to_s可以看出区别.
输出结果 :
再引用书上的一个例子 :
9. Public, Protected, Private Methods
class Myclass
def initialize(a)
@x = a
end
end
mobj1 = Myclass.new('digoal')
print(mobj1.inspect,"\n")
# 下面复写to_s方法
class Myclass
def to_s
puts("this ia a overrided to_s method.")
end
end
print(mobj1.inspect)
输出结果 :
#<Myclass:0x1de0f68 @x="digoal">
this ia a overrided to_s method.
再引用书上的一个例子 :
class MyClass
def sayHello
return "Hello from MyClass"
end
def sayGoodbye
return "Goodbye from MyClass"
end
end
class MyOtherClass < MyClass
def sayHello #overrides (and replaces) MyClass.sayHello
return "Hello from MyOtherClass"
end
# overrides MyClass.sayGoodbye but first calls that method
# with super. So this version "adds to" MyClass.sayGoodbye
def sayGoodbye
return super << " and also from MyOtherClass"
end
# overrides default to_s method
def to_s
return "I am an instance of the #{self.class} class"
end
end
9. Public, Protected, Private Methods
Ruby定义了3种方法的访问级别,
public
protected
private
我们前面定义的方法默认都是public的, 没有访问限制. 注意 initialize方法是private的.
protected和private方法都只能通过其他方法间接的访问, protected方法可以在public方法中被直接调用, 或间接调用(使用它自己的对象或,其他带有这个protected方法的父类或子类的对象调用) 来看个例子 :
执行结果 :
那么怎么来调用priv方法呢 :
执行结果 :
class MyClass
private
def priv
puts( "private" )
end
protected
def prot
puts( "protected" )
end
public
def pub
puts( "public" )
end
def useOb( anOb )
anOb.pub
anOb.prot # 调用它可以直接调用或通过同类的对象来调用,或具有prot方法的子类或父类来调用, 所以这里在后面的调用是可以的.
anOb.priv # 调用它的时候会报错, 因为private方法只能在public方法中直接调用.
end
end
mobj1 = MyClass.new
mobj2 = MyClass.new
mobj1.pub
# mobj1.prot # 直接调用失败
# mobj1.priv # 直接调用失败
mobj1.useOb(mobj1)
mobj1.useOb(mobj2)
执行结果 :
C:/Users/digoal/Desktop/new.rb:17:in `useOb': private method `priv' called for #<MyClass:0x1dc0b08> (NoMethodError)
from C:/Users/digoal/Desktop/new.rb:26:in `<main>'
public
public
protected
那么怎么来调用priv方法呢 :
class MyClass
private
def priv
puts( "private" )
end
protected
def prot
puts( "protected" )
end
public
def pub
puts( "public" )
end
def pub1
prot
priv
end
end
mobj1 = MyClass.new
mobj1.pub1
执行结果 :
protected
private
看起来有点多此一举, 所谓想让他私有化当然就不想直接被调用.
下面来看一个级联的类, 它在访问私有方法的时候是否允许?
执行结果 :
class Myclass
private
def priv
puts( "private" )
end
protected
def prot
puts( "protected" )
end
public
def pub
puts( "public" )
end
def pub1
pub
prot
priv
end
end
class Myclass1 < Myclass
private
def priv # 复写priv方法. 仍然可以被调用.
puts( "private1")
end
end
class Myclass2 < Myclass
end
执行结果 :
public
protected
private1
public
protected
private
pub1 方法可以定义在Myclass, 也可以定义在Myclass1和Myclass2. 都可以达到同样的目的 .
也就是说private方法只能在类定义的public方法中访问, 而不能通过对象直接访问.
protected方法则更加宽松, 可以通过public方法来直接调用或者间接调用(同类或具有这个protected方法的父类或子类的对象)
来看一个书上面的例子 :
private方法, 以下失败 :
protected方法, 最后一条失败 :
10. use send method invading the privacy of private method
class MyClass
private
def priv( aStr )
return aStr.upcase
end
protected
def prot( aStr )
return aStr << '!!!!!!'
end
public
def exclaim( anOb ) # calls a protected method
puts( anOb.prot( "This is a #{anOb.class} - hurrah" ) )
end
def shout( anOb ) # calls a private method
puts( anOb.priv( "This is a #{anOb.class} - hurrah" ) )
end
end
class MyOtherClass < MyClass
end
class MyUnrelatedClass
end
myob = MyClass.new
myotherob = MyOtherClass.new
myunrelatedob = MyUnrelatedClass.new
private方法, 以下失败 :
myob.shout( myotherob )
C:/Users/digoal/Desktop/new.rb:20:in `shout': private method `priv' called for #<MyOtherClass:0x1df04d8> (NoMethodError)
from C:/Users/digoal/Desktop/new.rb:35:in `<main>'
myotherob.shout( myob )
C:/Users/digoal/Desktop/new.rb:20:in `shout': private method `priv' called for #<MyClass:0x1e204f0> (NoMethodError)
from C:/Users/digoal/Desktop/new.rb:35:in `<main>'
protected方法, 最后一条失败 :
myob.exclaim( myotherob ) # This is OK
myotherob.exclaim( myob ) # And so is this...
myob.exclaim( myunrelatedob ) # But this won’t work
结果 :
C:/Users/digoal/Desktop/new.rb:16:in `exclaim': undefined method `prot' for #<MyUnrelatedClass:0x1d903a0> (NoMethodError)
from C:/Users/digoal/Desktop/new.rb:37:in `<main>'
This is a MyOtherClass - hurrah!!!!!!
This is a MyClass - hurrah!!!!!!
10. use send method invading the privacy of private method
调用方法还可以通过send方法, 以下是api的send解释 :
执行结果 :
class X
private
def priv( aStr, bStr )
puts("I'm private, " << aStr << bStr)
end
end
ob = X.new
# ob.priv( "hello" ) # This fails
ob.send( :priv, "hello", " world" ) # This succeeds
执行结果 :
I'm private, hello world
虽然这么做是可行的, 不过既然要直接调用还不如写成public方法呢.
11. Singleton Class Methods
在第5点的singleton method例子已经讲过了, 再次略过.
12. Nested Method
内嵌方法指的是在方法中嵌套方法, 一般用于一些在方法中可能重复执行的部分.
嵌套方法不能通过对象直接调用, 但是当先执行过最外层的方法后, 就可以直接调用了.
执行结果 :
class X
def outer_x
print( "x:" )
def nested_y
print("ha! ")
end
def nested_z
print( "z:" )
nested_y
end
nested_y
nested_z
end
end
ob1 = X.new
ob2 = X.new
# ob2.nested_z # 在它的外部方法被调用之前, 它不能被调用.
ob1.outer_x
print("\n")
ob2.nested_z # 虽然是ob1调用的外部方法 , ob2 也可以调用这个内嵌方法了.
执行结果 :
x:ha! z:ha!
z:ha!
13. Method Names
方法名, 协定是小写开头的.
就像常量我们一般是大写字母开头, class也是大写字母开头.
如果把方法名写成大写字母开头会怎么样呢? 来看个例子 :
1.9 不会有问题, 1.8会告诉你这个常量未定义 :
如果真定义了大写开头的方法, 并且遇到了这个错误怎么办呢? 加个括号来表示我要调用这个方法.
【参考】
class Myclass
def Method1 # 还是根据协定的语义来定义比较靠谱, 小写开头.
puts("this is a method")
end
end
mobj1 = Myclass.new
mobj1.Method1
mobj1.Method1() # 一般加个括号比较靠谱
The Book Of Ruby
Ruby 1.9.3 API