类上定义的方法是实例方法:
class Animal
def dog
"woof"
end
def cat
"meow"
end
end
它们之所以如此命名是因为它们响应类的任何实例,这意味着它们的接收者必须是类的实例:
Animal.instance_methods(false)
#=> [:dog, :cat]
animal = Animal.new
#=> #<Animal:0x00005bfb0c55ae98>
animal.dog
#=> "woof"
Animal.dog
#=> NoMethodError (undefined method `dog' for Animal:Class)
要定义一个接收者是一个类的方法(一个类方法),我们在类的单例类上定义该方法。对于 Animal 类,我们可以编写以下任一代码。
class Animal
class << Animal
def pig(n)
"#{n} little pigs"
end
end
end
Animal.methods(false)
#=> [:pig]
Animal.pig(3)
#=> "3 little pigs"
或
Animal.define_singleton_method(:pig) do |n|
"#{n} little pigs"
end
Animal.methods(false)
#=> [:pig]
Animal.pig(3)
#=> "3 little pigs"
class < Animal1 行将范围更改为 Animal 的单例类,导致 self 的值也更改为该类。
那么这和问题有什么关系,也就是定义方法def self.my_method ...呢?简短的回答是没有必要以这种方式定义方法。请耐心等待——我会解决的。
注意Animal的单例类上定义的方法pig被Animal的子类的单例类继承:
class Swine < Animal
end
Swine.instance_methods & [:dog, :cat]
#=> [:dog, :cat]
Swine.methods & [:pig]
#=> [:pig]
我们还可以在许多独特的对象上定义方法。考虑animal,Animal 的一个实例:
animal.define_singleton_method(:rodent) do |n|
"I'm rodent ##{n}"
end
animal.rodent(3241)
#=> "I'm rodent #3241"
animal 是此方法将响应的唯一接收者:
Animal.new.rodent(55)
#=> #NoMethodError (undefined method `rodent' for
# #<Animal:0x00005bfb0c530670>)
实际上,我们可以在每个具有单例类的对象上定义方法,也就是大多数对象:
str = "cow"
str.define_singleton_method(:greeting) { "moo" }
str.greeting
#=> "moo"
arr = [1,2]
arr.define_singleton_method(:greeting) { "I'm an array" }
arr.greeting
#=> "I'm an array"
module M; end
M.define_singleton_method(:greeting) { "I'm a module" }
M.greeting
#=> "I'm a module"
piggy = Animal.method(:pig)
#=> #<Method: Animal.pig>
piggy.define_singleton_method(:greeting) {
"I'm a singleton method" }
piggy.greeting
#=> "I'm a singleton method"
我们可以对所有具有单例类的 Ruby 对象执行此操作。这包括除了具有立即值(按值传递的对象)之外的所有对象,包括nil、true、false、整数、符号和一些浮点数。此外,已冻结的对象(例如,"Hi".freeze)没有单例类。
假设现在我们写
class Animal
def Animal.pig(n)
"#{n} little pigs"
end
end
或(相同的东西)
class Animal
def self.pig(n)
"#{n} little pigs"
end
end
(我们终于到了!)
这种定义方法的新方法是什么?实际上,这只是在Animal 的单例类上定义方法的一种简写方式。仅将其视为syntactic sugar。就像在方法定义的第一行写2 + 2 指示Ruby 执行2.+(2)、Animal. 或self. 只是指示Ruby 执行以下操作。
class Animal
class << self
def pig(n)
"#{n} little pigs"
end
end
end
也就是说,完全不需要写def Animal.my_method...或def self.my_method...来创建类方法;它只是为了方便 Ruby 编码人员。
1 该行一般写成class << self,当行执行时self 等于Animal 是可以接受的。如果类被重命名,使用<< self 只是一种方便。