单例类包含特定于单个对象的方法。
对于通用对象,这是一个不错的功能。但是对于课堂来说,这是至关重要的。让我们从对象开始:
对象的单例类
实例方法通常在类中定义。同一类的所有实例共享相同的实例方法。单例类位于对象及其类之间。它允许每个实例拥有自己的一组方法,独立于其他实例。
如果我们有两个类,Foo
并且Bar
每个类有 2 个实例a
,b
和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>
你会有这样的类结构:(简化,不包括模块)
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 懒惰地创建这些单例类,例如在调用singleton_class
.
所以在定义方法a.hello
的时候,并不是存放在a
的类Foo
中,而是存放在a
的单例类中:
def a.hello
'hello from a'
end
a.method(:hello).owner
#=> #<Class:#<Foo:0x00007fc280963008>> <-- a's singleton class
因此b
,即使两者都是Foo
实例,也看不到该方法:
b.hello #=> NoMethodError: undefined method `hello'
b
我们甚至可以在不干扰的情况下定义一个同名的方法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"
我们还可以定义一个泛型hello
inFoo
并在每个实例级别上覆盖它:(您通常不这样做,但这是可能的)
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"
单例类
以上对类尤其重要。每个类都是一个实例Class
:
Foo.class #=> Class
假设我们想要一个方法Foo.hello
,我们将在哪里定义它?
实例方法通常定义在实例的类中,所以我们可以在 的类中定义它Foo
:
class Class
def hello
'Hello from Foo'
end
end
Foo.hello
#=> "Hello from Foo"
但这将使该方法可用于所有实例Class
:
Bar.hello
#=> "Hello from Foo"
String.hello
#=> "Hello from Foo"
最好有一个Foo
实例专有的地方。那个地方是Foo
的单例类:
def Foo.hello
'Hello from Foo'
end
或者
class Foo
def self.hello # <-- self is Foo, so this is just "def Foo.hello"
'hello from Foo'
end
end
就像a.hello
上面一样,此方法仅适用于Foo
:
Foo.hello #=> "hello from Foo"
Bar.hello #=> NoMethodError
我们将这些方法称为类方法,但它们实际上只是单例类的实例方法:
Foo.method(:hello).owner
#=> #<Class:Foo> <-- Foo's singleton class
Foo.method(:hello).unbind == Foo.singleton_class.instance_method(:hello)
#=> true
如果您将类的单例方法与对象的单例方法进行比较,您会发现它们是相同的。这是因为在 Ruby 中,类也是对象,并且所有对象的工作方式都是一样的。