Singleton classes hold methods that are specific to a single object.
For generic objects, it's a nice-to-have feature. But for classes, it's crucial. Let's start with the objects:
Singleton classes for objects
Instance methods are usually defined in classes. All instances of the same class share the same instance methods. The singleton class sits between the object and its class. It allows each instance to have its own set of methods, independent of the other instances.
If we have two classes, Foo
and Bar
with 2 instances each a
, b
and c
, d
:
class Foo ; end
class Bar ; end
a = Foo.new #=> #<Foo:0x00007fc280963008>
b = Foo.new #=> #<Foo:0x00007f8319016b18>
c = Bar.new #=> #<Bar:0x00007fa66c8d7290>
d = Bar.new #=> #<Bar:0x00007f94d5106ac8>
You would have this class structure: (simplified, excluding modules)
object singleton class class superclass ...
a ── #<Class:#<Foo:0x00007fc280963008>> ─┐
├─ Foo ─┐
b ── #<Class:#<Foo:0x00007f8319016b18>> ─┘ │
├─ Object ── BasicObject
c ── #<Class:#<Bar:0x00007fa66c8d7290>> ─┐ │
├─ Bar ─┘
d ── #<Class:#<Bar:0x00007f94d5106ac8>> ─┘
Ruby creates those singleton classes lazily, for example when calling singleton_class
.
So when defining a method a.hello
, it is not stored in a
's class Foo
, but in a
's singleton class:
def a.hello
'hello from a'
end
a.method(:hello).owner
#=> #<Class:#<Foo:0x00007fc280963008>> <-- a's singleton class
Because of that, b
doesn't see that method, even though both are Foo
instances:
b.hello #=> NoMethodError: undefined method `hello'
And we can even define a method with the same name for b
without interfering with a
:
def b.hello
'hello from b'
end
b.method(:hello).owner
#=> #<Class:#<Foo:0x00007f8319016b18>> <-- b's singleton class
a.hello #=> "hello from a"
b.hello #=> "hello from b"
We could also define a generic hello
in Foo
and override it on a per instance level: (you usually don't do that, but it's possible)
class Foo
def hello
'hello'
end
end
def a.hello
"#{super} from a"
end
def b.hello
"b says #{super.upcase}!"
end
a.hello #=> "hello from a"
b.hello #=> "b says HELLO!"
c = Foo.new
c.hello #=> "hello"
Singleton classes for classes
The above is especially important for classes. Each class is an instance of Class
:
Foo.class #=> Class
Let's say we wanted to have a method Foo.hello
, where would we define it?
Instance methods are usually defined in the instance's class, so we could define it in Foo
's class:
class Class
def hello
'Hello from Foo'
end
end
Foo.hello
#=> "Hello from Foo"
But that would make the method available to all instances of Class
:
Bar.hello
#=> "Hello from Foo"
String.hello
#=> "Hello from Foo"
It would be better to have a place that's exclusive to the Foo
instance. And that place is Foo
's singleton class:
def Foo.hello
'Hello from Foo'
end
or
class Foo
def self.hello # <-- self is Foo, so this is just "def Foo.hello"
'hello from Foo'
end
end
Just like a.hello
above, this method is only available to Foo
:
Foo.hello #=> "hello from Foo"
Bar.hello #=> NoMethodError
We call these methods class methods, but they are really just instance methods of the singleton class:
Foo.method(:hello).owner
#=> #<Class:Foo> <-- Foo's singleton class
Foo.method(:hello).unbind == Foo.singleton_class.instance_method(:hello)
#=> true
And if you compare the singleton methods for classes with those for objects, you'll see that they are identical. That's because in Ruby, classes are objects too, and all objects work alike.
Class
class, if the eigenclass is really just a reference to theClass
object. Perhaps that helps? – Doggone