[ruby]单例类 singleton class,class_eval vs instance_eval

定义类

  • 类是一个增强的模块,类的知识都可以应用于模块
class A < Array
  def method
    'hello'
  end
end
# A.new.method => 'hello'
  • 同上
A = Class.new(Array) do
  def method
    'hello'
  end
end
# A.new.method => 'hello'

不用class关键字修改类 —— class_eval

def add_method_to(a_class)
# TODO: 在a_class上定义方法m
end
  • 知道类名的话,可以用class打开然后定义,比如a_class类名是A
class A
  def m; 'hello'; end
end

A.new.m => 'hello'
  • 不知道类名用class_eval
def add_methhod_to(a_class)
  a_class.class_eval do 
    def m;  'hello'; end
  end
end

add_method_to A
A.new.m => 'hello'
add_method_to String
'asd'.m => 'hello'

class_eval vs instance_eval

  • 使用def
class Hello end

Hello.instance_eval do 
  def fool_i
    puts "Hello instance_eval foo_i"
  end  
end  
 
Hello.class_eval do
  def foo_c
    puts "Hello class_eval foo_c"
  end  
end  

ls Hello
Hello.methods: foo_i 
Hello#methods: foo_c 

instance_eval 代码块中定义的方法变成了 Hello 的类方法,而 class_eval 代码块中定义的方法称为了 Hello 类的实例方法。这里有点绕,跟语义上边理解起来的相反。

  • 使用define_method
Hello.instance_eval do 
  define_method :fool_a do
    puts "Hello define_method instance_eval foo_a"
  end  
end  
 
Hello.class_eval do
  define_method :foo_b do
    puts "Hello define_method class_eval foo_b"
  end  
end  

ls Hello
Hello.methods: 
Hello#methods: foo_b  fool_a

instance_eval和class_eval中使用define_method定义的方法都是Hello的实例方法。

  • 加上singleton_class
Hello.singleton_class.instance_eval do 
  define_method :x do
    puts "Hello define_method instance_eval x"
  end  
end  
 
Hello.singleton_class.class_eval do
  define_method :y do
    puts "Hello define_method class_eval y"
  end  
end  

Hello.singleton_class.instance_eval do
  def h
    puts "Hello singleton_class class_eval def h"
  end  
end  

Hello.singleton_class.class_eval do
  def g
    puts "Hello singleton_class class_eval def g"
  end  
end 

ls Hello
Hello.methods: x y g h
Hello#methods: 

加上singleton_class后 instance_eval和class_eval中使用def 和 define_method定义的方法都是Hello的类方法。

  • 总结
    在这里插入图片描述

比如说有个类 G, 调用 class_eval 和 instance_eval 的时候 scope 是 G 这个 Class实例,也就是 self 是 G,所以 define_method 的 receiver 都是 G,所以定义的都是 instance method.

而 def 是在 current class 上定义 instant method,class_eval 中的 current class 自然是 G,instance_eval 中的 current class 是 G 的 singleton_class,所以是 class method.

类变量、类实例变量

  • 类变量可以被子类或者类的实例使用,类实例变量只可以被类本身访问,不能被类的实例或者子类访问。尽量使用类实例变量,不适用类变量
class A
  @v = 1 # 类实例变量
  @@v = 11 # 类变量
  def self.read
  	p @v
  	p @@v
  end
  def read
    p @v
    p @@v
  end
end

A.read => 1  11 
A.new.read => nil  11

类宏

class A
  attr_accessor :my_attribute
end

obj = A.new
obj.my_attribute = 'x'
obj.my_attribute => 'x'
  • 上面attr_accessor的本质是定义了两个普通的方法
class A
  def my_attribute=(value)
    @my_attribute = value
  end
  def my_attribute
    @my_attribute
  end
end

单例方法 singleton method

  • 只对单个对象定义的方法,称为单例方法,该方法只对这个对象生效。
str = 'just a regular string'
def str.title?
  # self => 'just a regular string'
  self.upcase == self
end 

str.title? => false
str.methods.grep(/title?/) => [:title?]
str.singleton_methods => [:title?]

单例类 singleton class

  • 单例类定义被用于定义对象的专属实例方法。
  • 在类中定义类方法, class << 类名 ~ end这种写法的类定义称为单例类定义,单例类定义中定义的方法称为单例方法 singleton method
# 3种定义类方法的方式
class C
  def C.hello
    "hello #{self}"
  end
end

class C
 def self.hello
   "hello #{self}"
 end
end

class C
  class << self   # class << C 一样, 表名单例类才是类方法真正所在之处
    def hello
       "hello #{self}"
     end
   end
end
p C.hello # =>  "hello C" 单例类方法
p C.singleton_class #=> #<Class:C>
p C.singleton_methods #=> [:hello]
# 接上面代码 
a = C.new
class << a #单例类
  def hello 
    self
  end
end

b = class << a
  self
end
p a.hello #=> #<C:0x00007fd3aa171760> C的实例
p a.singleton_class #=> #<Class:#<C:0x00007ffe10933b48>> 对单例类的引用方法一
p b #=> #<Class:#<C:0x00007fa6b6897bc8>> 对单例类的引用方法二
  • 不能用未定义的常量,或者变量去定义单例类
class << Hi  # class << hi
  def hello
     "hello #{self}"
  end
end
NameError (uninitialized constant Hi)  # NameError (undefined local variable or method `hi' for main:Object)
  • 单例类,单例方法,单例类方法,实例方法,单例类的超类,超类的单例类
class C
  class << self
    def a_class_method
      'C.a_class_method'
    end
  end
end

class D < C
  def a_method
    'D#a_instance_method'
  end
end
obj = D.new
obj1 = D.new

class << obj #单例类
  def a_method
    'obj#a_singleton_method'
  end
end

p obj.a_method #=> "obj#a_singleton_method"  说明实例找方法时候会先找单例类方法
p obj1.a_method #=> "D#a_instance_method"
p obj.class.superclass #=> C
p obj.singleton_class #=> #<Class:#<D:0x00007fcf51134390>>
p obj.singleton_class.superclass #=> D 也是obj的类 obj.class
p obj.singleton_methods #=> [:a_method]  单例方法
p D.singleton_class #=> #<Class:D> 
p D.super_class #=> C
p D.singleton_class.superclass #=> #<Class:C> D的单例类的超类是C的单例类
p D.superclass.singleton_class #=> #<Class:C> D的超类的单例类是C的单例类
p D.a_class_method #=> 'C.a_class_method' 调用D的单列方法,找到了继承的C的单例方法
  • ruby 对象模型七条规则
  1. 只有一种对象——要么是普通对象,要么是模块。
  2. 只有一种模块——可以是一个普通模块、一个类或者一个单例类。
  3. 只有这一种方法,它存在于一个模块中——通常是在一个类中。
  4. 每个对象(包括类)都有自己的“真正的类”——要么是一个普通类,要么是一个单例类。
  5. 除了BasicObject类没有超类外,每个类有且只有一个祖先——要么是一个类,要么是一个模块。这意味着任何类只有一条向上的、直到BasicObject的祖先链。
  6. 一个对象的单例类的超类是这个对象的类。一个类的单例类的超类是这个类的超类的单例类。
  7. 调用一个方法时,Ruby先向右进入接收者真正的类,然后向上进入祖先链。这就是Ruby查找方法的方式。
# 初始化变量
str1 = 'Ruby'   # String的一个对象
str2 = 'Ruby'  # String的一个对象
str3 = 'ruby'  # String的一个对象
str4 = [1,2]  # Array的一个对象
str5 = {a:6}  # Hash的一个对象
str6 = 1  # Numeric的一个对象
str7 = Numeric.new
  • str1是String的一个实例对象,可以定义单例类
class << str1 #单例类
  def hello 
    "hello #{self}"
  end
end
p str1.hello # => "hello Ruby"
p str2.hello # => NoMethodError: undefined method `hello' for "Ruby":String
  • 需要把实例先赋值给一个变量,再定义单例类,否则无法调用
class << "Ruby" 
  def hello 
    "hello #{self}"
  end
end
无法调用
p "Ruby".hello # => NoMethodError (undefined method `hello' for "Ruby":String)
  • str3是String的一个实例对象,可以定义单例类
class << str3 #单例类
  def hello 
    "hello #{self}"
  end
end
p str3.hello # => "hello ruby"
  • str4是Array的一个实例对象,可以定义单例类
class << str4 #单例类
  def hello 
    "hello #{self}"
  end
end
p str4.hello # => "hello [1, 2]"
  • str5是Hash的一个实例对象,可以定义单例类
class << str5 #单例类
  def hello 
    "hello #{self}"
  end
end
p str6.hello # => "hello {:a=>6}"
  • str6是Integer的一个实例对象,不能 定义单例类
class << str6 
  def hello 
    "hello #{self}"
  end
end
TypeError (can't define singleton)
  • str7是Numeric.new的一个实例对象,不能 定义单例类
class << str7
  def hello 
    "hello #{self}"
  end
end
TypeError (can't define singleton method "hello" for Numeric)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值