Techioz Blog

Rubyのシングルトンクラスとは何ですか?

概要

Rubyのeigenclassまたはsingletonクラスの概念を理解するのに苦労しています。 eigenclass はクラスのクラスであるとよく読みました。すべてのクラスが実際にはクラス Class のインスタンスであるため、私にとってクラスのクラスは実際には Class であるため、それは私には意味がありません。

私がよく理解できないもう 1 つの点は、次の文です。クラス メソッドは実際にはクラス eigenclass のインスタンス メソッドです。 eigenclass には次の方法でアクセスできます。

YourClass = Class.new
class << YourClass
  def class_method
  end
end

しかし、eigenclass が実際に YourClass クラス (つまり Class) である場合、前のコード部分でクラス Class を開き、それにインスタンス メソッド class_method を追加して、将来のすべてのインスタンス (通常のクラス) にアクセスできるようにすべきではないでしょうか。将来定義される)?

実際のところ、シングルトン クラスはクラスと同じではないような気がします。実行するとき:

class MyClass
end

MyClass.singleton_class

# が得られますが、これは MyClass.class => Class の出力とは異なります。

# の出力は何ですか?これは名前空間とは関係ありません。そうでない場合は、Class::MyClass… の 2 つが存在することになります。

私の考えを明確にするために、eigenclass の概念についてのシンプルで明確な説明を探しています。

解決策

シングルトン クラスは、単一のオブジェクトに固有のメソッドを保持します。

一般的なオブジェクトにとって、これはあると便利な機能です。しかし、授業にとって、それは非常に重要です。オブジェクトから始めましょう:

インスタンス メソッドは通常、クラス内で定義されます。同じクラスのすべてのインスタンスは、同じインスタンス メソッドを共有します。シングルトン クラスは、オブジェクトとそのクラスの間に位置します。これにより、各インスタンスが他のインスタンスから独立した独自のメソッドのセットを持つことができます。

2 つのクラス 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

そのため、両方とも Foo インスタンスであるにもかかわらず、 b にはそのメソッドが表示されません。

b.hello #=> NoMethodError: undefined method `hello'

また、a に干渉することなく、b に同じ名前のメソッドを定義することもできます。

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"

また、Foo で汎用 hello を定義し、それをインスタンス レベルでオーバーライドすることもできます (通常はこれを行いませんが、可能です)。

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"

ただし、これにより、クラスのすべてのインスタンスでメソッドが使用できるようになります。

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 ではクラスもオブジェクトであり、すべてのオブジェクトが同様に機能するためです。