3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

インスタンスを理解して、インスタンスメソッド・クラスメソッドの違いを確認する

Posted at

はじめに

メソッドの定義や使い方をコードリーディングしていると、「これは何のメソッドだろう?」と迷うことがよくあります。自分自身、インスタンスメソッドとクラスメソッドの違いをあまり意識せずに読んでることもありました。
この記事では、インスタンスとは何かという基本から振り返り、インスタンスメソッドとクラスメソッドの違いを簡単に解説します。メソッドの定義方法や、具体的な呼び出し方を確認しながら、理解を深めていきます。

目次

  1. インスタンスとは
  2. インスタンスメソッドの定義の仕方
  3. インスタンスメソッドの使い方
  4. インスタンスメソッドとインスタンス変数
  5. クラスメソッドの定義の仕方
  6. クラスメソッドの使い方
  7. クイズ
  8. まとめ

インスタンスとは

プログラム上でオブジェクトのひな形(設計図)がクラスです。
クラスからオブジェクトをつくることをインスタンス化といいます。
そして、クラスから作られた(インスタンス化した)オブジェクトのことをインスタンスといいます。

image.png

大事なので図で説明します。

image.png

↑↑↑↑これがインスタンスです!!

  • インスタンスは具体的な個々のオブジェクトを指す
  • インスタンスは、クラスで定義されたインスタンスメソッドを呼び出すことができる。
  • 継承したクラスのインスタンスは、スーパークラスで定義されたインスタンスメソッドを呼び出すことが出来る。
  • インスタンス変数を保持している。

インスタンスメソッドの定義の仕方

では、次にインスタンスメソッドの定義の仕方を確認します。

    class Animal
      def initialize(name)
        @name = name
      end
    
      def speak
        "#{@name} makes a sound"
      end
    end

このコードでは、Animal クラスを定義し、initialize メソッドでインスタンス変数 @name を初期化します。speak メソッドがインスタンスメソッドであり、インスタンスごとに異なる動作を行います。

インスタンスメソッドの使い方

    class Animal
      def initialize(name)
        @name = name
      end
    
      def speak
        "#{@name} makes a sound"
      end
    end
    # Animalクラスのインスタンス化とインスタンスメソッド呼び出しの例
    dog = Animal.new("Dog")
    puts dog.speak  # => Dog makes a sound
    
    cat = Animal.new("Cat")
    puts cat.speak  # => Cat makes a sound

<クラス名>.new(引数)でインスタンス化することが出来ます。
作成したインスタンスに.メソッド名を付けてあげることでクラスで定義したインスタンスメソッドを使えるようになります。

今回の例だと、Animal.new("Dog")Animal クラスのインスタンスを生成し、dog.speak でインスタンスメソッドが呼び出され、インスタンス変数 @nameにアクセスします。

インスタンスメソッドとインスタンス変数

インスタンスメソッドをもう少し理解するためにメソッドの探索経路についてのお話をします。
インスタンスメソッドをきちんと理解するためには継承を無視することはできません。インスタンスは作成元のクラスに定義してあるインスタンスメソッドだけではなく、親クラスのメソッドも使えるからです。

image.png

インスタンスメソッドを実行すると、そのインスタンスが属するクラスに指定されたメソッドが存在するかどうかを判定します。属するクラスにメソッドがない場合は、さらにひとつ抽象度を上げたスーパークラスにメソッドを探しに行きます。
そして最後まで見つからなかった場合は、NoMethodErrorが発生するという感じです。

image.png

そしてちなみにの話をすると、メモリ上でインスタンスメソッドは、クラスオブジェクトに、インスタンス変数はインスタンスに保持されます。
インスタンスメソッドはクラスオブジェクトのメソッドテーブルに格納され、インスタンスがメソッド呼び出しを行う際に、メソッド探索がクラスのメソッドテーブルで行われます。

インスタンスメソッドはクラスオブジェクトに保持される
    #クラスオブジェクトで呼び出し
    p Animal.instance_methods
    # => [:hash, :singleton_class, :dup, :itself, :methods,
    #:singleton_methods, :protected_methods, :private_methods,
    #:public_methods, :instance_variables, :instance_variable_get,
    #:instance_variable_set, :instance_variable_defined?, 
    #:remove_instance_variable, :instance_of?, :kind_of?, :is_a?,
    #:display, :public_send, :class, :tap, :frozen?, :yield_self,
    #:then, :extend, :clone, :<=>, :===, :!~, :method, :public_method, 
    #:nil?, :singleton_method, :eql?, :respond_to?, 
    #:define_singleton_method, :freeze, :inspect, :object_id, :send,
    #:to_s, :to_enum, :enum_for, :!, :equal?, :__send__, :==, :!=, 
    #:__id__, :instance_eval, :instance_exec]

    #インスタンスで呼び出し
    p p dog.instance_methods 
    #(NoMethodError)
インスタンス変数はインスタンスに保持される
    #クラスオブジェクトで呼び出し
    p Animal.instance_variables
    #  => [] 

    #インスタンスで呼び出し
    p dog.instance_variables
    #  => [:@name] 

インスタンス変数は、インスタンスごとに個別に保持されるデータです。インスタンスメソッドは、そのインスタンスが保持するインスタンス変数にアクセスして、データを操作します。
見にくいですが、下の図のような感じになります。

image.png

クラスメソッドの定義の仕方

    class Animal
      @@count = 0
    
      def initialize(name)
        @name = name
        @@count += 1
      end
    
      def self.get_count
        "Total animals: #{@@count}"
      end
    end

このコードでは、self.get_count というクラスメソッドを定義しています。このメソッドはクラス全体に対して動作します。
クラスメソッドはインスタンスメソッドと異なり、メソッド名にself.を付けます。

クラスメソッドの使い方

# クラスメソッド呼び出しの例

class Animal
  @@count = 0

  def initialize(name)
    @name = name
    @@count += 1
  end

  def self.get_count
    "Total animals: #{@@count}"
  end
end

puts Animal.get_count # => Total animals: 0 

# インスタンスを作成するとカウントが増える
lion = Animal.new("Lion") 
tiger = Animal.new("Tiger") 

puts Animal.get_count # => Total animals: 2
  • self.get_count はクラスメソッドであり、インスタンスを生成せずに呼び出せます。
  • クラスメソッドは、Animal.get_count のようにクラスから直接呼び出します

とりあえず、クラスメソッドはインスタンスメソッドと異なり、インスタンス作成を必要としません。

クイズ

animals = Animal.all

Q : このとき、animalsはインスタンスですか?
理由付きで答えられるでしょうか?
考えてからスクロールしてみて下さい。


答えはNOです。
animals 自体はインスタンスではなく、コレクションActiveRecord::Relationオブジェクト)で、配列の各要素が Animal インスタンスです。(ActiveRecord::Relationに関してはまた別の記事を書こうと思います。)

Ruby on Rails の ActiveRecord モデルクラスで、<クラス名>.allクラスメソッドとして定義されていて、クラスオブジェクトに対して呼び出すと、インスタンスの配列が返されるようになっています。

そのため、animalsでインスタンスメソッドを呼び出そうとすると、エラーが発生します。このときのanimalsActiveRecord::Relationオブジェクト(Arrayのように振る舞う)だからです。いくらクラスを遡っても、speakメソッドは見つかりません。

一応確認すると、

# animals は ActiveRecord::Relation オブジェクト
animals = Animal.all
puts animals.class # => ActiveRecord::Relation

コレクションに対しては、each でループすることで個々のインスタンスにアクセスできるようになります。

animals.speak
NoMethodError: undefined method `speak' for #<Animal::ActiveRecord_Relation:0x00007f9d4a1d2b10>
# 各インスタンスのメソッドを呼び出し
animals.each do |animal| 
    puts animal.speak
end

image.png

あくまで、インスタンスメソッドは個別の情報(インスタンス変数)を持ったインスタンスをレシーバーとして呼び出すことが出来るのであり、インスタンスコレクションは対象になりません。

まとめ

  • インスタンス:クラスから生成されたオブジェクトのこと
  • インスタンスメソッド: インスタンスをレシーバーとして呼び出すメソッドです。主にインスタンス変数を操作し、個々のオブジェクトの状態に依存する処理を行う
  • クラスメソッド: クラス自体をレシーバーとして呼び出すメソッドです。インスタンスの状態には依存せず、クラス全体に関わる処理を行う

端的な言葉で説明できるか否かは理解度の大きな判断基準になりそうです。
インスタンス・インスタンスメソッド・クラスメソッドの理解の助けになれば幸いです。

参考文献

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?