Ruby元编程-学习笔记(四)-类定义

来源:互联网 发布:oracle恢复原来的数据 编辑:程序博客网 时间:2024/05/21 07:59

类定义

在类或模块定义时,其自身充当了当前对象self的角色,类和模块也都是对象,与方法和块相同,类定义也会返回最后一条语句的值.

class MyClass    puts "Hello"end=> Hello
  • 1
  • 2
  • 3
  • 4
  • 5

当前类

尽管self可以获得当前对象,但并不能获得当前类,每当通过class关键字打开一个类时,这个类就成为当前类.

class MyClass    # 现在当前类是MyClass    def my_method        # my_method是MyClass的一个实例方法    endend
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

class关键字必须指定类名才可以打开类,对于未知类名就可以修改当前类,可以使用class_eval().

class_eval()

Module#class_eval()方法会在一个已存在类的上下文中执行一个块.

def add_method_to(a_class)    a_class.class_eval do        def my_method do            "Hello!"        end    endendadd_method_to String"str".my_method         # => "Hello!"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

class_eval()方法与instance_eval()方法并不相同, instance_eval()方法仅会修改self(特定情况下也会修改当前类),而\calss_eval()方法会同时修改self和当前类. 
对于二者的选择,若只想打开一个对象,并不在乎是不是类,使用instance_eval更好;而若是希望打开类,则使用class_eval()更好.

类实例变量

由于类是Class的实例,类名是常量,故而当self由当前类充当时所定义的变量即为类实例变量.

class MyClass    @my_var = 1    def self.read; @my_var; end    # self为当前类MyClass    def write; @my_var = 2; end    # self为调用该方法的接收者    def read; @my_var; endendobj = MyClass.newobj.writeobj.read            # => 2MyClass.read        # => 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

类变量

类变量与实例变量不同, 它们可以被子类或类的实例所引用, 类似java静态成员.

@@v = 1class MyClass    @@v = 2end@@v     # => 2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这种情况不就破坏了class域作用门的性质, 得到这种结果是因为类变量并不真正属于类,它们属于类体系结构, 由于@@v定义与man顶级作用域中,它属于main的类Object,所以也属于Object所有的后代,而MyClass继承自Object,因此也共享了这个类变量.

单件方法

Ruby允许给单个对象增加一个方法, 该方法只对这个对象有效,称为单件方法.

str1 = "abc"str2 = "abc"def str1.single_method    "singleton_method!"endstr1.class                  # => Stringstr1.single_method          # => "singleton_method!"str2.class                  # => Stringstr2.single_method          # => Error:NoMethodError
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

从上述代码可以看出同样是String类对象的str1和str2, str1有single_method方法,而str2没有, 还记得前面讲过,对象保存的只是一组实例变量和它自身类的引用,对象的方法都保存在自身类中,很明显single_method方法并不在str2的祖先链中, 那么single_method方法去哪了?下面的Eigenclass则告诉了我们答案.

Eigenclass

在obj上定义一个单件方法, 它存放在哪, 同样类是Class的对象, 而类方法不能被类的实例对象调用,说明类方法不在Class中,那么又存放在哪里呢? 
当一个对象索要它的类时,Ruby并没有告诉我们全部,我们得到的类并不是看到的类,而是一个对象特有的隐藏类,我们称之为eigenclass.

obj = Object.neweigenclass = class << obj    self    # => Classenddef obj.my_singleton_method; endeigenclass.instance_methods.grep(/my_/)  # => ["my_singleton_method"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在查找方法时,会先从接收者的eigenclass中查找,若未找到,则在eigenclass的超类中查找. 
一个对象的eigenclass的超类是这个对象的类;一个类的eigenclass的超类是这个类的超类的eigenclass.

module MyModule    def self.my_method; 'hello'; endendclass MyClass    include MyModuleendMyClass.my_method       # => Error: NoMethodError
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

当类包含模块时,它获得的是该模块的实例方法而不是类方法,类方法存在与模块的eigenclass中. 那么如何通过包含模块来定义类方法.

module MyModule    def my_method; 'hello'; end    # 首先在模块中定义普通实例方法endclass MyClass    class << slef        # 通过打开MyClass的eigenclass来包含模块        include MyModule    endendMyClass.my_method       # => 'hello'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

my_method()方法是MyClass的eigenclass的一个实例方法,这样my_method()也是MyClass的一个类方法,这种技术称为类扩展.同样该技术运用在对象中则是对象扩展.

类宏

在Java中要想访问一个类的私有属性需要get和set方法,虽然Ruby对象并没有属性,但如果想像get和set一样访问,就会定义两个拟态方法.

class MyClass    def set=(value)        @attr = value    end    def get        @attr    endend
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这样的写法会给人很枯燥的感觉,我们可以通过Module#attr_*()方法家族中的成员来定义访问器.Module#attr_reader()可以生成一个读方法, Module#attr_writer()可以生成一个写方法,Module#attr_accessor()可以同时生成两者. 
所有的attr_*()方法都定义在Module中,所以不过self是类还是模块,都可以使用它们.类似与attr_accessor()的方法我们称之为类宏, 虽然类宏看起来像关键字,但它们只是普通方法,只是可以用在类定义中.

方法别名

通过使用alias关键字,可以给Ruby方法取一个别名.

class MyClass    def my_method        "my_method()"    end    alias :m :my_method  # 第一个是新名字endobj = MyClass.newobj.my_method           # => "my_method()"obj.m                   # => "my_method()"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

环绕别名

class String    alias :real_length :length    def length        real_length > 5 ? "long" : "short"    ednend"This a test example".length        # => "long""This a test example".real_length   # => 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上述代码虽然重写了length方法,但是别名方法还是引用的原始方法. 
当重定义一个方法时,并没有真正修改这个方法,而是把当前存在的这个方法名与新定义的方法绑定起来, 只要旧方法还存在绑定的名字,仍然可以调用.旧的方法被新的方法所环绕,这种技巧称为环绕别名,步骤如下:.

1.给方法定义一个别名 
2.重定义这个方法 
3.在新的方法中调用老的方法

原创粉丝点击