Ruby之self详解(一)详细剖析

来源:互联网 发布:datax导入数据到hive 编辑:程序博客网 时间:2024/06/06 11:32
Ruby之self详解(一)详细剖析
    博客分类:
  • Ruby_and_Rails
CC++C#RubyITeye

 

简单来说,ruby中的self的含义,要看其上下文。

 

    self上下文

     Ruby的self有和Java的this相似之处,但又大不相同。Java的方法都是在实例方法中引用,所以this一般都是指向当前对象的。而 Ruby的代码逐行执行,所以在不同的上下文(context)self就有了不同的含义,先来看看常见的context self都代表哪些

Ruby代码  
  1. # 这个位置位于top level context,代表Object的默认对象main   
  2. self # => main   
  3. self.class # => Object   
  4. @self1 = self  
  5.   
  6. # 因为所有自定义类都位于main context之中,所以这是Object的实例方法   
  7. # 同时也可以说是一个全局方法   
  8. def a_method   
  9.   @self2 = self  
  10.   p self    
  11.   # => main,因为有了实例变量@self1和@self2,所以打印出来的不是main这个字符   
  12.   # => 但仍然是main对象,注释掉4,8行即可看到效果   
  13.   p @self1 == @self2 # => true   
  14. end  
  15.   
  16.   
  17. # 下面是一个关于类中不同上下文的self   
  18. class Person   
  19.   p self # => Person,代表当前类   
  20.      
  21.   def instance_method   
  22.     p self # => #<Person:0xb7818fdc>,代表当前类的实例   
  23.   end  
  24.      
  25.   def self.class_method   
  26.     p self # => Person,和第16行一样代表当前类(这是类方法的context),它们是相等的   
  27.   end  
  28. end  
  29.   
  30. m = Person.new  
  31. def m.hello   
  32.   p self # => 代表m这个单例对象   
  33. end  
  34.   
  35. m.hello  
# 这个位置位于top level context,代表Object的默认对象mainp self # => mainp self.class # => Object@self1 = self# 因为所有自定义类都位于main context之中,所以这是Object的实例方法# 同时也可以说是一个全局方法def a_method @self2 = self p self # => main,因为有了实例变量@self1和@self2,所以打印出来的不是main这个字符 # => 但仍然是main对象,注释掉4,8行即可看到效果 p @self1 == @self2 # => trueend# 下面是一个关于类中不同上下文的selfclass Person p self # => Person,代表当前类 def instance_method p self # => #<Person:0xb7818fdc>,代表当前类的实例 end def self.class_method p self # => Person,和第16行一样代表当前类(这是类方法的context),它们是相等的 endendm = Person.newdef m.hello p self # => 代表m这个单例对象endm.hello

     上面只写了在类中的self,其实在module也是一样的。通过上面代码你可以发现,self一直引用着它所在位置上下文的实例 (类也是一个实例)(其实这是跟java等一样的,合乎面向对象的要求,self/this指向自己) 。

 

    self显式/隐式

    你可以先试着运行下面代码,看看有什么意外发生没有

Ruby代码  
  1. class Person   
  2.   attr_accessor :name  
  3.      
  4.   def set_name(your_name)   
  5.     name = your_name    
  6.   end  
  7. end  
  8.   
  9. m = Person.new  
  10. p m.name   
  11. m.set_name('today')   
  12. p m.name # => 猜是什么  
class Person attr_accessor :name def set_name(your_name) name = your_name endendm = Person.newp m.namem.set_name('today')p m.name # => 猜是什么

      如果你猜是today就大错特错了,答案是nil,为什么是nil呢,在第5行,我明明调用的是attr_accessor生成的name=方法赋值的啊,你可以在前面加上self试试,代码如你预期的一样执行了。在这种情况下name = your_name并没有去调用attr_accessor生成的xx=方法,而是将name当作了一个局部变量,如果显式的指定self,就没有问题了。

(另外,请注意,ruby中的实例变量要有@号,所以此处不用self,而用@name,也是可以的)

 

    读到这,你是不是认为以后这种情况就一直用显式self去调用就好了,其实不然,下面的代码仍会说明一些问题

Ruby代码  
  1. class Person   
  2.   
  3.   public   
  4.   def get_my_secret1   
  5.     my_secret # => 隐式   
  6.   end  
  7.      
  8.   def get_my_secret2   
  9.     self.my_secret # => 显式   
  10.   end  
  11.   
  12.   private   
  13.   def my_secret   
  14.     p 'something...'  
  15.   end  
  16.      
  17.   def self.secret   
  18.     p 'nothing'  
  19.   end  
  20.   
  21.   class << Person   
  22.     def method3   
  23.       p 'method3'  
  24.     end  
  25.   
  26.     private   
  27.     def method4   
  28.       p 'method4'  
  29.     end  
  30.   end  
  31.   
  32. end  
  33.   
  34. m = Person.new  
  35. #m.my_secret # => private method error    
  36. Person.secret # => nothing   
  37.   
  38. Person.method3 #fantaxy added here!   
  39. Person.method4 #fantaxy added here! #NoMethodError: private method `method4' called for Person:Class   
  40.   
  41. m.get_my_secret1 # => something   
  42. m.get_my_secret2 # => private method error  
class Person public def get_my_secret1 my_secret # => 隐式 end def get_my_secret2 self.my_secret # => 显式 end private def my_secret p 'something...' end def self.secret p 'nothing' end class << Person def method3 p 'method3' end private def method4 p 'method4' end endendm = Person.new#m.my_secret # => private method error Person.secret # => nothingPerson.method3 #fantaxy added here!Person.method4 #fantaxy added here! #NoMethodError: private method `method4' called for Person:Classm.get_my_secret1 # => somethingm.get_my_secret2 # => private method error

      上面代码说明:

    第一个问题,显式self不可以调用private(protected的也一样)方法,而隐式的可以(这个原因我在下面解释) 。

原因是什么?

Ruby代码  
  1.  self.my_secret # => 显式   
  2. #这一句的self在实例方法内,所以self指的是Person的一个实例,而实例是不能调用私有方法的(protected也如此)   
  3. #注意,此时的self和我们new一个Person的实例没有本质区别。  
self.my_secret # => 显式#这一句的self在实例方法内,所以self指的是Person的一个实例,而实例是不能调用私有方法的(protected也如此)#注意,此时的self和我们new一个Person的实例没有本质区别。

 

    第二个问题,本来:权限修饰符只对实例方法生效(下面解释),但是这里类方法也受限制了。

解释:

ruby中类也是一个实例,给类这个实例加入实例方法,也会有private/protected之分。

Ruby代码  
  1. class << Person #给实例(Person)添加实例方法   
  2.   def method3   
  3.     p 'method3'  
  4.   end  
  5.   
  6.   private   
  7.   def method4   
  8.     p 'method4'  
  9.   end  
  10. end  
class << Person #给实例(Person)添加实例方法 def method3 p 'method3' end private def method4 p 'method4' end end

 

 

    self“怪异”写法

    下面代码被我个人称为怪异写法,因为平时用不到,但偶尔会看到,但看起来又不太直观,这里列举一下

 

Ruby代码  
  1. class Person   
  2.   def metaclass   
  3.     class << self #这是ruby中单例的语法,其本质是生成一个self的单例类(虚类)|域   
  4. #因为上面的self在实例方法内,因此指的是Person的一个实例,所以会生成实例的单例类(不是类的单例类)   
  5. #下面返回的self,是在单例类的作用域内,所以指的是其本身,即单例类本身(虚类)   
  6. # 有些人比较迷惑,我打印出self是Person啊,这是由于ruby的实现问题,并不打印虚类本身,   
  7. # 而是打印虚类的super类的类名,此处即Person类(X类的对象x,x的虚类的super指向X)         
  8.       self  
  9.     end  
  10.   end  
  11.      
  12.   def metaclass2   
  13.     self #实例作用域   
  14.   end  
  15. end  
  16.   
  17. a = Person.new  
  18.   
  19. b = a.metaclass   
  20. c = a.metaclass2   
  21.   
  22. # 首先要明白,类Person是Class的一个“实例”,a是Person的一个实例   
  23. # 这里b也是一个Person类,但它是独一无二的,即你修改Person不会影响到b,反之亦然   
  24. p b # => #<Class:#<Person:0xb76f3800>>   
  25. p b.class # => Class   
  26.   
  27. class Person   
  28.   def hello   
  29.     p 'hello Person'  
  30.   end  
  31. end  
  32.   
  33. class << b   
  34.   def hello   
  35.     p 'hello b'  
  36.   end  
  37. end  
  38.   
  39. b.hello # => hello b   
  40.   
  41.   
  42. p c # => #<Person:0xb76f3800>   
  43. p c.class # => Person   
  44.   
  45. c.hello # => hello Person  
class Person def metaclass class << self #这是ruby中单例的语法,其本质是生成一个self的单例类(虚类)|域#因为上面的self在实例方法内,因此指的是Person的一个实例,所以会生成实例的单例类(不是类的单例类)#下面返回的self,是在单例类的作用域内,所以指的是其本身,即单例类本身(虚类)# 有些人比较迷惑,我打印出self是Person啊,这是由于ruby的实现问题,并不打印虚类本身,# 而是打印虚类的super类的类名,此处即Person类(X类的对象x,x的虚类的super指向X)      self end end def metaclass2 self #实例作用域 endenda = Person.newb = a.metaclassc = a.metaclass2# 首先要明白,类Person是Class的一个“实例”,a是Person的一个实例# 这里b也是一个Person类,但它是独一无二的,即你修改Person不会影响到b,反之亦然p b # => #<Class:#<Person:0xb76f3800>>p b.class # => Classclass Person def hello p 'hello Person' endendclass << b def hello p 'hello b' endendb.hello # => hello bp c # => #<Person:0xb76f3800>p c.class # => Personc.hello # => hello Person

 

Ruby代码  
  1. class Person   
  2.   def self.hello   
  3.     p 'hello'  
  4.   end  
  5.      
  6.   class << self  
  7.     # 看了最上面self和context的关系,你应该知道这个self代表是Person类   
  8.     # 在这里为Person添加方法,其实也就是为Person添加类方法,和上面的self.hello异曲同工   
  9.     def work   
  10.       p 'hard work'  
  11.     end  
  12.   end  
  13. end  
  14.   
  15. Person.work  
class Person def self.hello p 'hello' end class << self # 看了最上面self和context的关系,你应该知道这个self代表是Person类 # 在这里为Person添加方法,其实也就是为Person添加类方法,和上面的self.hello异曲同工 def work p 'hard work' end endendPerson.work

 

 

明白了self之后,看看下面的用法:ruby 的 extend self

 

我们想让实例方法同时为类方法,那么可以使用extend self,这个对于普通的类 
可能没有什么用。但是对于module来说还是很有用的,因为module不可以实例化, 
module的实例方法通过自身就无法单元测试,所以通过extend self可以作为类方法暴露 
来测试了:

 

Ruby代码  
  1. module M   
  2.   extend self  
  3.   def greeting   
  4.     puts "hi"  
  5.   end  
  6. end  
module M extend self def greeting puts "hi" endend


如果没有extend self,我们就无法使用M.greeting, 
现在我们可以调用M.greeting了。 
BTW: 
module_function可以把module一个实例方法变成私有的,并复制一份放到其metaclass中。

 

 

参考:

http://ilstar.blogbus.com/logs/59782933.html

http://fuliang.iteye.com/blog/827443

#Ruby
原创粉丝点击