Rubyのsetterメソッドは特別扱いされる。

Rubyのsetterメソッドの振る舞いを理解していなくて、ちょっとビックリしたので。

まずはsetterの基本から。

def abc=(a)
  puts "abc setter"
end

abc = 123

これは何も表示しません。abc = 123はローカル変数abcを定義しています。

def abc=(a)
  puts "abc setter"
end

self.abc = 123

こうすると"abc setter"が表示されます。同じようにgetterを足してみると……

def abc=(a)
  puts "abc setter"
end

def abc
  puts "abc getter"
end

self.abc = 123
self.abc

最後の行でエラーになります。

private method `abc' called for main:Object (NoMethodError)

トップレベルのメソッドはデフォルトでプライベートなので、self.で修飾して実行できません。でもsetterは特別。self.abc = 123と書けます。

同じことをクラスを定義して試してみると、

class Abc
  def test
    self.xyz=123
    xyz
  end

private

  def xyz=(a)
    puts "xyz setter"
  end

  def xyz
    puts "xyz getter"
  end
end

o = Abc.new
o.test
o.xyz = 123

結果は以下の通り。

$ ruby ./tmp.rb
xyz setter
xyz getter
./tmp.rb:21: private method `xyz=' called for #<Abc:0x1002fb10> (NoMethodError)

ポイントは、

  • self.xyz=123 を xyz=123 とするとローカル変数の定義になり、setterは呼ばれない(privateかどうかに関わらず)。
  • xyz を self.xyzに変えるとエラー。setterとgetter(普通のメソッド)は扱いが違う。
  • 最後の o.xyz = 123 はエラー。privateなxyz=に対して、中で self.xyz=123 と書けても、外から o.xyz=123 とは書けるわけではない。

まあprivateなsetter/getterを使う機会はあんまり無いかもしれませんが……。

参考

Thu Feb 20 04:07:06 2003  Nobuyoshi Nakada  <nobu.nokada@softhome.net>

	* parse.y (attrset): "self.foo=x" can be legal even when "foo="
	  is private.