かずおの開発ブログ(主にRuby)

日々の開発のことを色々書きます。

Rubyのメソッドについてあらためてしっかり理解する

今回はメソッドについてです。 Rubyに限らずほぼ全ての言語には

を定義可能です。 今回はこれらについて詳しく見ていきます。

パブリックと プライベートメソッドの違い

こちらはわかっている方が大多数かと思いますが、改めて確認です。 パブリックメソッドはクラス外から呼び出せる公的なメソッド、 プライベートメソッドはクラス内でしか呼び出せない私的なメソッドです。

class A
  def some_public_method
    puts "this is public instance method"
  end

  def call_private_method
    some_private_method
  end

  private
  def some_private_method
    puts "this is private instance method"
  end
end

# パブリックメソッド(呼び出せる)
A.new.some_public_method
# => this is public instance method

# プライベートメソッド(呼び出せない)
A.new.some_private_method
# => private method `private_instance_method' called for #<A:0x007fe0b204e300> (NoMethodError)

# 内部から呼び出すことは可能
A.new.call_private_method
# => this is private instance method

プライベートメソッドが呼び出せないのはRubyの仕組みとして * privateメソッドにレシーバーをつけてはいけない * 自分(self)以外のオブジェクトのメソッドを呼び出すときはレシーバをつけなければいけない という2つのルールがコンフリクトしてしまうためです。 参考(url)

インスタンスメソッドとクラスメソッド

こちらも基本です。クラスメソッドはクラスから直接呼び出せるメソッドインスタンスメソッドはクラスからnewで作られたインスタンスから呼び出せるメソッドです。( Rubyではクラスもオブジェクトなのでクラスメソッドはクラスのインスタンスメソッドとも言えるかと思います 。)

class B
  def some_instance_method
    puts "this is instance method"
  end
  def self.some_class_method
    puts "this is class method"
  end
end

# インスタンスメソッドはクラスをnewしたインスタンスから呼び出せる
B.new.some_instance_method
# => this is instance method

# クラスメソッドはクラスから直接呼び出せる
B.some_class_method
# => this is class method

# インスタンスからクラスメソッドは呼び出せrない
B.new.some_class_method
# => undefined method `some_class_method' for #<B:0x007f8cc4113b60> (NoMethodError)

# クラスからインスタンスメソッドは呼び出せない
B.some_instance_method
# => undefined method `some_instance_method' for B:Class (NoMethodError)

パブリック・プライベートとクラス・インスタンスメソッドをそれぞれ組み合わせたもの

今度はそれぞれを組み合わせたものを試してみます

class C
  def some_public_instance_method
    puts "this is public instance method"
  end

  def self.some_public_class_method
    puts "this is public class method"
  end

  private
  def some_private_instance_method
    puts "this is private instance method"
  end

  def self.some_private_class_method
    puts "this is private class method"
  end
end

C.some_private_class_method
# => this is private class method

とは言っても今までで出てきていないのはプライベートなクラスメソッドのみです。ここで不思議なことが起きるのですが、プライベートなクラスメソッドはクラスの外から呼び出せてしまいます。つまりパブリックなクラスメソッドと同じ動きをしてしまいます。 ちょっとこれはかなり不思議な事態なのですが恐らく * privateメソッドにレシーバーをつけてはいけない =>(クラス側から見ればself.some_private_class_method=C.some_private_classmethodと解釈されてレシーバーはついていない) * 自分(self)以外のオブジェクトのメソッドを呼び出すときはレシーバをつけなければいけない =>(呼び出し側から見ればCがレシーバー) となって呼び出せてしまうのかなと思います。あくまでこれは僕の推測ですのでご注意ください。(もしご存知の方いらっしゃいましたら、コメントでご教示いただけると幸いです。)

本当にプライベートなクラスメソッドを定義するにはprivate_class_methodを使う必要があります。

class D

  def self.some_private_class_method
    puts "hello"
  end
  private_class_method :some_private_class_method
end

D.some_private_class_method
# => private method `some_private_class_method' called for D:Class (NoMethodError)

これで意図した動作になりました。