2010年9月13日月曜日

Rubyのprivateメソッドルールの例外

privateメソッドのルール

『メタプログラミングRuby』P.65のコラムに、
privateメソッドのルールについて書かれていました。

 ”プライベートメソッドには1つのシンプルなルールが適用される。それは、「明示的なレシーバをつけてプライベートメソッドを呼び出すことはできない」というものだ。つまり、プライベートメソッドは、暗黙的なレシーバselfに対するものでなければならない。”

「privateメソッドは、明示的なレシーバをつけられない」。
レシーバがない状態といえば、「(暗黙的に)selfをレシーバとする状態」しかないわけですから、
通常はそのprivateメソッドを持っているオブジェクトからじゃないとメソッドを参照する経路がありません。
これによってRubyのprivateメソッドが「プライベートな」存在になるのですね。
(本気を出せば破ってしまえますが、それはまた別の話。)

これはわかりやすい覚え方です。
具体的に、極端な例を試してみると以下のようになります。

class A
  def foo
    self.bar
  end

  private

  def bar
    puts "bar is private method"
  end
end

A.new.foo # => error

例え同じクラス内からでも、明示的にレシーバ(ここではself)を
書いてしまうとエラーが起きます。

ルールの例外

しかし、このルールには例外が一つあります。
それは「セッターメソッドには明示的なレシーバが必要」、ということです。
(セッターメソッドというのは、メソッド名が「=」で終わるもののこと)

先程の例を、privateメソッドをセッターメソッドに変えてやってみましょう。

class B
  def foo
    self.bar = 1
  end

  private

  def bar=(arg)
    puts "bar= is private method"
  end
end

B.new.foo # => "bar= is private method"

この通りエラーが起きません。
privateメソッドがセッターメソッドの場合には、
明示的にレシーバを書いてもよいのです。

というよりも、明示的にレシーバを書かないとおかしなことになるからです。
以下にそれを示します。

class C
  def foo
    bar = 1 # ただのローカル変数の代入と見なされる
  end

  private

  def bar=(arg)
    puts "bar= is private method"
  end
end

C.new.foo # => 当然何も起こらない

セッターメソッドには明示的なレシーバが必要。
また、それはメソッドがprivateでもpublicでも変わることはありません。

この、privateメソッドのルールの例外については、
Twitterで @n0kada さんに教えていただきました。ありがとうございました。
http://twitter.com/n0kada/status/11625643920

2 件のコメント:

  1. セッターの件を Stuff To Be Considered in the Next Edition
    http://pragprog.com/titles/ppmetr/errata
    に入れてもらいましたので、増刷があれば修正されるはずです。

    返信削除
  2. わわ、コメントありがとうございます!
    そうですか、次刷では修正されるのですね。

    メタプログラミングRuby、ほんとに良い本なので、
    もっとたくさん売れるよう願っております!

    返信削除

フォロワー