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
AI 代码解读
执行结果 :
This is a class method This is an instance method
AI 代码解读
以下是错误的 :
MyClass.instanceMethod #=> Error! This is an 'undefined method' ob.classMethod #=> Error! This is an 'undefined method'
AI 代码解读
既然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
AI 代码解读
执行结果 :
/root/rubyscript/test.rb test.rb . .rb 2012-03-06 12:10:44 +0800 265 bytes
AI 代码解读
2. Class Variables
前面的章节已经介绍了几种变量 : 全局变量, 本地变量, 实例变量, 类变量.
类变量以@@开头, 作用域是类以及它的对象. 需要声明的是, 类变量必须在创建类时被初始化(可以为nil).
因为所有的Class都是Class的对象, 所以类也可以有它的实例变量.
再来看一个例子, 可以更清晰的知道, 类的实例变量存储的值是在它本身的, 而不是它的对象. 它的对象的实例变量的值可以通过initialize方法来初始化.
class Myclass @@c1 = nil # 初始化时可以给nil # @@c1 # 已经讲过, 类变量使用前必须初始化, 所以注释掉的这行是有问题的. @i1 def Myclass.get puts(@@c1) end end
AI 代码解读
再来看一个例子, 可以更清晰的知道, 类的实例变量存储的值是在它本身的, 而不是它的对象. 它的对象的实例变量的值可以通过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
AI 代码解读
以上代码执行完后这个MyClass就有了一个类变量和一个实例变量, 初始值都是1000.
其中类变量的作用域是它和它的对象. 而实例变量的作用域仅仅是它自己.
下面来操作一下这个类 :
执行结果 :
for i in 0..2 do ob = MyClass.new MyClass.classMethod ob.instanceMethod puts( MyClass.showVars ) puts( ob.showVars ) end
AI 代码解读
执行结果 :
(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
AI 代码解读
从结果上印证了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
AI 代码解读
执行结果 :
1000 -------------- MyClass @instvar=1000 ob @instvar= 1 MyClass @instvar=1000 ob @instvar= 2 MyClass @instvar=1000 ob @instvar= 3
AI 代码解读
把# MyClass.classMethod这行的注释去掉, 重新执行 :
1000 -------------- MyClass @instvar=1010 ob @instvar= 1 MyClass @instvar=1020 ob @instvar= 2 MyClass @instvar=1030 ob @instvar= 3
AI 代码解读
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
AI 代码解读
输出全是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()
AI 代码解读
报错, 因为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>'
AI 代码解读
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)
AI 代码解读
那么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 )
AI 代码解读
执行结果 :
HELLO WORLD String
AI 代码解读
那么把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))
AI 代码解读
执行结果 :
#<MyClass:0x1ee0e98> MyClass hello world nil
AI 代码解读
显然在new方法中定义的@anewvar它的值并没有初始化到ob对象的@anewvar实例变量上.
那么它到哪里去了呢 ? 原来初始值在类里面 . 也就是说要初始值到类的对象里面, 我们必须在initialize中初始它.
5. Singleton Methods
puts(MyClass.instance_variable_get(:@anewvar)) 执行结果 : HELLO WORLD
AI 代码解读
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
AI 代码解读
执行结果 :
[: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.
AI 代码解读
我们来看看系统内置的类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]
AI 代码解读
如何来判断一个对象是否有某个singleton method呢, 我们前面已经通过singleton_methods返回了一个Array类型的对象.
那么我们可以通过Array的方法来判断是否有某个元素, 这样来判断这个方法是否存在于你要找的对象中.
例如 :
或者直接使用respond_to?方法 :
p File.singleton_methods.include?(:new) 结果 : true
AI 代码解读
或者直接使用respond_to?方法 :
p File.respond_to?(:new) 结果 : true
AI 代码解读
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)
AI 代码解读
执行结果 :
Array :: about to recurse with aClass.superclass = Object Object :: about to recurse with aClass.superclass = BasicObject BasicObject :: about to recurse with aClass.superclass = nil
AI 代码解读
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
AI 代码解读
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)
AI 代码解读
输出结果 :
#<Myclass:0x1de0f68 @x="digoal"> this ia a overrided to_s method.
AI 代码解读
再引用书上的一个例子 :
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
AI 代码解读
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)
AI 代码解读
执行结果 :
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
AI 代码解读
那么怎么来调用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
AI 代码解读
执行结果 :
protected private
AI 代码解读
看起来有点多此一举, 所谓想让他私有化当然就不想直接被调用.
下面来看一个级联的类, 它在访问私有方法的时候是否允许?
执行结果 :
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
AI 代码解读
执行结果 :
public protected private1 public protected private
AI 代码解读
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
AI 代码解读
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>'
AI 代码解读
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!!!!!!
AI 代码解读
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
AI 代码解读
执行结果 :
I'm private, hello world
AI 代码解读
虽然这么做是可行的, 不过既然要直接调用还不如写成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 也可以调用这个内嵌方法了.
AI 代码解读
执行结果 :
x:ha! z:ha! z:ha!
AI 代码解读
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() # 一般加个括号比较靠谱
AI 代码解读
The Book Of Ruby
Ruby 1.9.3 API