开发者

Extending classes and instances

This question has two parts.

In the Ruby Programming Language book, there is an example (section 8.1.1) of extending a string object and class with a module.

First question. Why is it that if you extend a class with a new method, and then create an object/instance of that class, you cannot access that method?

irb(main):001:0> module Greeter; def ciao; "Ciao!"; end; end
=> nil
irb(main):002:0> String.extend(Greeter)
=> String
irb(main):003:0> String.ciao
=> "Ciao!"
irb(main):004:0> x = "foo bar"
=> "foo bar"
irb(main):005:0> x.ciao
NoMethodError: undefined method `ciao' for "foo bar":String
        from (irb):5
        from :0
irb(main):006:0>

Second part, When I try to extend a Fixnum object, I get an undefined method error. Can someone explain why this works for a string but not a fixnum?

irb(main):045:0> module Greeter开发者_运维技巧; def ciao; "Ciao!"; end; end
=> nil
irb(main):006:0> 3.extend(Greeter)
TypeError: can't define singleton
        from (irb):6:in `extend_object'
        from (irb):6:in `extend'
        from (irb):6


First question. Why is it that if you extend a class with a new method, and then create an object/instance of that class, you cannot access that method?

Because you extended the String class, so #ciao is a class method and not an instance method.

String.send(:include, Greeter)
x = "foo bar"
x.ciao
# => "Ciao!"

Second part, When I try to extend a Fixnum object, I get an undefined method error. Can someone explain why this works for a string but not a fixnum?

Here's the short answer.

"Fixnums, Symbols, true, nil, and false are implemented as immediate values. With immediate values, variables hold the objects themselves, rather than references to them.

Singleton methods cannot be defined for such objects. Two Fixnums of the same value always represent the same object instance, so (for example) instance variables for the Fixnum with the value "one" are shared between all the "ones" is the system. This makes it impossible to define a singleton method for just one of these."

Off course, you can include/exten the Fixnum class and every Fixnum instance will expose the methods in the Mixin. This is exactly what Rails/ActiveSupport does in order to allow you to write

3.days.ago
1.hour.from_now


obj.extend(module) adds all the methods in module to obj. When called as String.extend(Greeter) you are adding the methods from Greeter to the instance of Class that represents String.

The easiest way to add additional instance methods to an existing class is to re-open the class. The following examples do the same thing:

class String
  include Greeter
end

class String
  def ciao
    "Ciao!"
  end
end

Fixnums (as well as Symbols, true, false and nil) are handled in a different way to normal instances. Two Fixnums with the same value will always be represented by the same object instance. As a result, Ruby doesn't allow you to extend them.

You can of course extend an instance of any other class, e.g.:

t = "Test"
t.extend(Greeter)
t.ciao
=> "Ciao!"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜