类定义
注意: 类也是对象
当前类
- 在程序顶层,当前类为main对象的类 Object
- class 打开一个类时(或者打开一个module),打开的类即当前类
- 在一个方法中,当前类即当前对象的类
# 顶层的类为Object,若在此定义方法则为Object的实例方法
class MyClass # class 打开的MyClass 即为当前类
def my_method # MyClass为当前类
end
end
class_eval 方法
class 限制: 需要知道类名
class_eval 不需要知道类名就能打开当前类
Module#class_eval会在已存在类的上下文中执行一个块
def add_method_to a_class # 此时当前类为Object
a_class.class_eval do # 当前类变更 a_class
def test_a
'class_eval会修改当前类和self'
end
end
end
add_method_to String
"a".test_a # => 'class_eval会修改当前类和self'
对比:
class:
- 打开类需要指定类的名字,而且必须时常量
- class 是作用域门,使用class会失去局部绑定
Module#class_eval: - 任何代表类的变量都可以使用class_eval
- class_eval使用的是代码块,扁平作用域,可以获取局部绑定
单例方法
ruby 允许给单个对象增加一个方法
只对单个对象生效的方法-单例方法
str = 'this is a singleton_method'
def str.title?
str.update == str
end
str.title? # => false
str.methods.grep(/title?/) #=> [:title?]
str.singleton_methods #=> [:title?]
这一段代码单独为str添加一个title? 方法,其他的对象是没有这个方法的
单例方法也可以用Object#define_singleton_method 来定义
类方法
类也是一个对象
类名只是常量
an_obj.a_method
AClass.a_class_method
类方法的调用和对象实例方法的调用语法是一样的,所以类方法的实质就是: 类方法是一个类的单例方法
类宏
attr_accessor 是一个类(Module类)的类方法
当一个模块引入这个模块的时候自动帮我们拓展出这个方法
示例: ruby 内核中的方法 attr_accessor
class MyClass
def my_attribute=(value)
@my_attribute = value
end
def my_attribute
@my_attribute
end
end
obj = MyClass.new
obj.my_attribute= 'whj'
obj.my_attribute # => 'whj'
可以用 Module#attr_*来定义访问器
所有的attr_* 方法都定义在Module中,不管self是类还是模块,都可以使用方法,像这样的方法称之为类宏
单件类(元类/本征类)
一个对象的单例方法没有存在obj中,因为obj不是一个类,也没有存在obj的类中,因为这样obj类的所有实例都能共享这个单例方法
单件类是一个对象特有的隐藏类
Object#class 会将元类隐藏起来
如何进入单件类:
- class的特需语法
class << an_obj
#do_something
end
如果想要获得单件类的引用,需在离开作用域时返回self
obj = Object.new
singleton_class = class < obj
self
end
singleton_class.class # => Class
- Object#singleton_class 能够获取单件类的引用
单件类是对象的单件方法存放的地方
每个单件类只有一个实例,而且不能被继承
单件类和模块结合
类扩展
一个class 引入一个module时,只引入了module的实例方法,不会引入类方法
解决:
将类方法当初一个普通的实例方法,然后在class的单例类中引入这个module-类扩展
module MyModule
def a_method
"hello, whj"
end
end
class MyClass
class << self
include MyModule
end
end
MyClass.a_method # => 'hello, whj'
对象扩展
module MyModule
def a_method
"hello, whj"
end
end
class MyClass
end
obj = MyClass.new
class << obj
include MyModule
end
obj.a_method # => 'hello, whj'
方法包装器
包(GEM)中的方法不能直接修改,希望能够为这个方法附加一个额外的属性, 所有的客户端都会自动获得这个属性
Module#alias_method 别名
class MyClass
def my_method
'hello, whj'
end
end
alias_method :m, :my_method
obj = MyClass.new
obj.my_nethod # => 'hello, whj'
obj.m # => 'hello, whj'
环绕别名
- 给方法重定义一个别名
- 重定义这个方法
- 在新的方法中调用老的方法
细化包装器
下包含包装器 Module#prepend (较常用)
prepend包含的模块会插入该类祖先链的下方, 意味着它可以重写该类的同名方法
class MyClass
def my_method
"a"
end
end
module MyModule
def my_method
"b" #也可以使用super 来继承
end
end
MyClass.class_eval do
prepend MyModule
end
obj = MyClass.new
obj.my_method # => "b"
网友评论