ALLSPICE

スパイスファクトリー株式会社のメンバーが運営するWeb開発メディア

Ruby において class << self; end でクラスメソッドを定義できる理由を解説

Posted by ShintoTatsuo | |システム開発
Ruby において class << self; end でクラスメソッドを定義できる理由を解説

クライアント企業のDXを推進しているスパイスファクトリー株式会社、Ruby on Rails エンジニアの田中です。弊社の詳しいサービス内容はアジャイル開発に関するページをご覧ください。

今回は、クラスメソッドを定義する時に用いる class << self; end の記法について、この記述でクラスメソッドを定義できる理由を説明したいと思います。
私自身が Ruby を学び始めた時に理解につまずいた部分でもあるため、この記事が同じような悩みをお持ちの方にとって参考になれば幸いです。

Ruby ではクラスもオブジェクトとして扱われる

Ruby 3.0.0 リファレンスマニュアルで述べられる通り、「Ruby で扱える全ての値はオブジェクト」であるという前提をまずは理解する必要があります。
これはつまり、クラス自身もオブジェクトであるということです。

例えば、以下の2種類のクラス定義はほとんど同義と言えます。


# class を用いる場合
class Human
  def hello
    puts 'hello'
  end
end
human = Human.new
human.hello #=> hello

 


# Class クラスのインスタンスを生成し、定数 Human に保有させる場合
Human = Class.new do
  def hello
    puts 'hello'
  end
end
human = Human.new
human.hello #=> hello

 

全てのインスタンスには必ず特異クラス(シングルトンクラス)が存在

次に、Ruby では、どんなインスタンスにも必ず特異クラスが存在するということを理解する必要があります。

そもそも、この「特異クラス」とは何でしょうか。
特異クラスとは、特定のインスタンスのみに適用されるクラスの事を指します。
そしてクラスメソッドというのは、あるクラスの特異クラスに定義されたメソッドのことを指しているのです。

より深く理解するために、特異クラスとインスタンスやクラスがどんな関係にあるのかをみていきましょう。

まずはごく単純に、インスタンスとクラスの関係を考えてみます。
これらの関係は下図のようになっています。
child インスタンスの class が Child クラスということです。


コードにすると次のようになります。


class Child
  def child_method
    puts 'This method is "child_method"'
  end
end
child = Child.new

puts child.class #=> Child
puts Child.instance_methods(false) #=> child_method

 

次に親クラスを導入してみます。
これらの関係は下図のようになっています。
Child クラスの superclass が Parent クラスということです。


コードにすると次のようになります。


class Parent
  def parent_method
    puts 'This method is "parent_method"'
  end
end
parent = Parent.new

class Child < Parent 
 def child_method 
  puts 'This method is "child_method"' 
 end 
end 
child = Child.new
 
puts parent.class #=> Parent
puts Parent.instance_methods(false) #=> parent_method

puts child.class #=> Child
puts Child.superclass #=> Parent
puts Child.instance_methods #=> child_method, parent_method, ・・・(省略)

 

最後に、特異クラスを導入してみます。特異クラスには以下の特徴があります。

  • シングルトンクラスとも言われ、1つのインスタンスしか持たない。
  • Rubyでは、どんなインスタンスにも必ず特異クラスが存在する

言葉で表現すると分かりにくいですが、つまりは次の図のようになっているということです。

コードにすると次のようになります。


class Parent
 # (省略)
end
parent = Parent.new
 
class Child < Parent
 # (省略)
end 
child = Child.new
 
puts child.singleton_class #=> <Class:#<Child:0x00007fb3e9aa7338>> ( #<Child:0x00007fb3e9aa7338> は「 Child クラスのインスタンス」を表す )
puts child.singleton_class.superclass #=> Child
 
puts Child.singleton_class #=> <Class:Child>
puts Child.singleton_class.superclass #=> <Class:Parent>
 
puts Parent.singleton_class #=> <Class:Parent>

 

特異クラスとは何か、インスタンスやクラスとどんな関係にあるのか、ご理解いただけたでしょうか。

class < <object; end 内のメソッド定義 = object の特異クラスへのメソッド定義

ここまで、Rubyではクラスもオブジェクトであることや、全てのインスタンスには必ず特異クラス(シングルトンクラス)が存在することを説明してきました。

ここからは、class << object; end のスコープにメソッド定義することで、 object の特異クラスにメソッド定義できることについて説明していきます。

まず基本的な話ですが、メソッドはクラスに保有されていきます。

例えばクラス内で def; end を用いて定義したメソッドは、そのクラスのインスタンスメソッドとして定義されます。

そしてこれがこの記事の本題なのですが、 class << object; end のスコープ内にメソッド定義すると、その object の特異クラスにメソッドが定義されます。

つまり以下であるということです。


class Child
  # Child クラスのインスタンスメソッドを定義
  def child_method
    puts 'This method ("child_method") is instance_method'
  end
end
child = Child.new
child2 = Child.new

# class << object; end の記法で、 child2 の特異クラスのインスタンスメソッドを定義
class << child2 
  def child2_method 
    puts 'This method ("child2_method") is singleton_method, which is only accessible from child2'
  end 
end 

child.child_method #=> This method ("child_method") is instance_method
# child からはアクセスできない
child.child2_method #=> undefined method `child2_method' for # (NoMethodError)

child2.child_method #=> This method ("child_method") is instance_method
# child2 からはアクセスできる
child2.child2_method #=> This method ("child2_method") is singleton_method, which is only accessible from child2

puts Child.instance_methods(false) #=> child_method
puts child2.singleton_class.instance_methods(false) #=> child2_method

 

先程、クラスもオブジェクトであるという事をお伝えしました。
さらにクラス定義における self は、例えば Child クラスの場合「Class クラスのインスタンスである Child オブジェクト自身」を指しています。

以上の2点を踏まえると、 class << object; end の記法を用いて、次のようにクラスメソッドを定義できます。


class Child
  def child_method
    puts 'This method ("child_method") is instance_method'
  end

  # object の部分に self を使用
  # class << Child でも同義であるが、一般的に self も用いた記述が好まれる
  class << self 
    def child_class_method
      puts 'This method ("child_class_method") is class_method'
    end 
  end 
end 
child = Child.new
 
child.child_method #=> This method ("child_method") is instance_method
Child.child_class_method #=> This method ("child_class_method") is class_method

 

このようにして、class << self; end を用いてクラスメソッドを定義することができました。

最後に

以上が class << self; end でクラスメソッドを定義できる理由の説明です。
拙い記事を最後まで読んでいただきありがとうございました。この記事が少しでも理解の助けになれば幸いです。

また、弊社では開発からサービスの運用まで、幅広くご支援させていただいております。詳しいサービス内容に関しては、当社ホームページのアジャイルシステム開発に関するページをご参照ください。

無料相談はこちらから
 

補足:クラスメソッド定義には self.method; end という記述もある

1点補足として、クラスメソッド定義には self.method; end という記法もあります。
どちらの記法が良いかについては色んな意見があるようなので、気になる方は調べてみてください。


# この記事で説明した記法(「特異クラス形式」と呼ばれる)
class Child
  (省略)

  class << self
    def child_class_method
      (省略)
    end
  end
end

# self.method; end を用いた記法(「特異メソッド形式」と呼ばれる)
class Child
  (省略)

  def self.child_class_method
    (省略)
  end
end

 

このエントリーをはてなブックマークに追加
Tags
About The Author

ShintoTatsuo

何かお困りのことはありませんか?無料でご相談を承っております!