给定的
module Foo
def bar
puts "foobar"
end
end
我能做到
String.extend(Foo)
因此
String.bar # => "foobar"
为什么这不起作用?:
a = String.new
a.bar # => NoMethodError: undefined method `bar' for "":String
是因为'a'现在和实例和.extend仅对类方法有效吗?为什么它会失去我通过.extend给出String的'新'功能?
答案 0 :(得分:6)
Ruby允许您以两种方式将模块中的方法添加到类中:extend
和include
。
使用您提供的模块:
module Foo
def bar
puts "foobar"
end
end
使用extend
方法将这些方法添加为类方法,并且可以直接在类对象本身上调用:
class Bar
extend Foo
end
Bar.bar # => "foobar"
或者,您可以在类范围之外调用它:
class Bar
end
Bar.extend Foo
Bar.bar # => "foobar"
include
略有不同。它会将方法添加为实例方法。它们只能在类的实例上调用:
class Bar
include Foo
end
Bar.bar # NoMethodError
a = Bar.new
a.bar # => "foobar"
关键的区别在于,在我们调用实例方法之前,我们首先必须创建一个实例a
。
正如sepp2k所说,extend
可以在任何Object上调用,而不仅仅是Class对象。例如,你可以去:
class Bar
end
a = Bar.new
a.extend Foo
a.bar # => "foobar"
但是,bar
只会添加到单个实例a
中。如果我们创建一个新实例,您将无法调用它。
b = Bar.new
b.bar # => MethodNotFoundError
答案 1 :(得分:0)
在Ruby中,类String是一个对象,通过执行String.extend(Foo),您可以为String类对象创建一个单例类,并在其中包含模块Foo(这意味着您要将类方法添加到字符串类对象,因此可以调用String.bar
)。完全停止。
a.bar
不起作用,因为没有实例方法栏。
不,你没有通过extend给String赋予新内容。
答案 2 :(得分:0)
Object#extend
方法向接收者添加参数模块定义的方法。
你必须记住String
类本身就是一个对象(类型为Class
),因此当你用它作为接收者调用“extend”时,那个类对象(String)本身获取新方法。
因此,在您的第一个示例String.extend(Foo)
中,将类实例添加到函数“bar”中。也就是说,“bar”现在是 shadow类(又名Singleton类)的实例方法,因此对象String
(它是一个类)现在具有方法“bar”。这与将类方法添加到String类(例如def String.bar; puts 'foobar'; end
)的效果大致相同,因此它对String类型的实例没有影响。