首先, class << foo
语法打开了foo
的单例类(eigenclass)。这允许您专门化在该特定对象上调用的方法的行为。
a = 'foo'
class << a
def inspect
'"bar"'
end
end
a.inspect # => "bar"
a = 'foo' # new object, new singleton class
a.inspect # => "foo"
现在,回答这个问题: class << self
打开self
的单例类,这样就可以为当前的self
对象重新定义方法(在类或模块体内部是类或模块本身 )。通常,这用于定义类 / 模块(“静态”)方法:
class String
class << self
def value_of obj
obj.to_s
end
end
end
String.value_of 42 # => "42"
这也可以写成速记:
class String
def self.value_of obj
obj.to_s
end
end
甚至更短:
def String.value_of obj
obj.to_s
end
在函数定义中, self
指的是调用函数的对象。在这种情况下, class << self
打开该对象的单例类; 一个用途就是实现穷人的状态机:
class StateMachineExample
def process obj
process_hook obj
end
private
def process_state_1 obj
# ...
class << self
alias process_hook process_state_2
end
end
def process_state_2 obj
# ...
class << self
alias process_hook process_state_1
end
end
# Set up initial state
alias process_hook process_state_1
end
因此,在上面的示例中, StateMachineExample
每个实例都将process_hook
别名为process_state_1
,但请注意后者如何将process_hook
(仅限self
,不影响其他StateMachineExample
实例)重新定义为process_state_2
。因此,每次调用者调用process
方法(调用可重定义的process_hook
)时,行为都会根据它所处的状态而改变。
我在这篇博客中找到了关于class << self
, Eigenclass
和不同类型的methods
的超级简单解释。
在 Ruby 中,有三种类型的方法可以应用于类:
实例方法和类方法几乎与其他编程语言中的同义词类似。
class Foo
def an_instance_method
puts "I am an instance method"
end
def self.a_class_method
puts "I am a class method"
end
end
foo = Foo.new
def foo.a_singleton_method
puts "I am a singletone method"
end
访问Eigenclass
(包括单例方法)的另一种方法是使用以下语法( class <<
):
foo = Foo.new
class << foo
def a_singleton_method
puts "I am a singleton method"
end
end
现在你可以为self
定义一个单例方法,在这个上下文中就是类Foo
本身:
class Foo
class << self
def a_singleton_and_class_method
puts "I am a singleton method for self and a class method for Foo"
end
end
end
通常,实例方法是全局方法。这意味着它们可以在定义它们的类的所有实例中使用。相反,单个方法是在单个对象上实现的。
Ruby 在类中存储方法,所有方法都必须与类相关联。定义单例方法的对象不是类(它是类的实例)。如果只有类可以存储方法,那么对象如何存储单例方法?创建单例方法时,Ruby 会自动创建一个匿名类来存储该方法。这些匿名类称为元类,也称为单例类或特征类。单例方法与元类相关联,而元类又与定义单例方法的对象相关联。
如果在单个对象中定义了多个单例方法,则它们都存储在同一个元类中。
class Zen
end
z1 = Zen.new
z2 = Zen.new
class << z1
def say_hello
puts "Hello!"
end
end
z1.say_hello # Output: Hello!
z2.say_hello # Output: NoMethodError: undefined method `say_hello'…
在上面的例子中,类 << z1 将当前 self 改为指向 z1 对象的元类; 然后,它定义了元类中的 say_hello 方法。
类也是对象(称为 Class 的内置类的实例)。类方法只不过是与类对象关联的单例方法。
class Zabuton
class << self
def stuff
puts "Stuffing zabuton…"
end
end
end
所有对象都可能有元类。这意味着类也可以有元类。在上面的例子中,类 << self 修改 self,因此它指向 Zabuton 类的元类。如果定义的方法没有显式接收器(将在其上定义方法的类 / 对象),则在当前作用域内隐式定义它,即 self 的当前值。因此,stuff 方法是在 Zabuton 类的元类中定义的。上面的示例只是定义类方法的另一种方法。恕我直言,最好使用 def self.my_new_clas_method 语法来定义类方法,因为它使代码更容易理解。上面的例子被包含在内,所以我们理解当我们遇到类 << self syntax 时发生的事情。
有关 Ruby Classes 的更多信息可以在这篇文章中找到。