(2022.6.4追記)
以下の情報はRuby 2.4時代の情報である点にご注意ください。
Ruby 2.7以降ではふつうのprivateメソッドもself付きで呼べるようになるなど、最新のRubyでは仕様が若干変わっています。
なお、拙著「プロを目指す人のためのRuby入門 改訂2版」ではRuby 3.0に対応して、こうした新しい言語仕様についても詳しく説明しています。こちらもぜひご覧ください。
はじめに
この記事は書籍「プロを目指す人のためのRuby入門」に掲載できなかったトピックを著者自らが紹介するアドベントカレンダーの17日目です。
本文に出てくる章番号や項番号は書籍の中で使われている番号です。
今回はprivateメソッドをレシーバ付きで呼び出せるケースを説明します。
必要な前提知識
「プロを目指す人のためのRuby入門」の第7章まで読み終わっていること。
privateメソッドをレシーバ付きで呼び出せるケース
7.7.2項でも説明したとおり、Rubyのprivateメソッドはself
キーワードを付けて呼び出すことができません。
class User
def hello
# nameメソッドはprivateなのでselfを付けるとエラーになる
"Hello, I am #{self.name}."
end
private
def name
'Alice'
end
end
user = User.new
user.hello #=> NoMethodError: private method `name' called for #<User:0x007fb18a3903d0>
一方、7.5.1項ではname=
のようなセッターメソッドを呼び出すときは必ずself
を付ける必要があると説明しました。
class User
attr_accessor :name
def rename_to_bob
# メソッド内でセッターメソッドを呼び出す場合はselfを必ず付ける
self.name = 'Bob'
# selfを付けないとローカル変数への代入と見なされる
name = 'Carol'
end
end
では、もしもセッターメソッドがprivateメソッドだったらどうなるんでしょうか?セッターメソッドはself
を付けて呼び出す必要がありますし、privateメソッドはself
を付けて呼び出すことができません。完全に矛盾してしまいます。
class User
attr_accessor :name
# セッターメソッドのみprivateにする
private :name=
def rename_to_bob
# name=を呼び出したいが、privateメソッドなのでselfを付けられない?
self.name = 'Bob'
end
end
うーん、これは困りました・・・と思いきや、実はセッターメソッドの場合はprivateメソッドでもself
付きで呼び出すことができます。
class User
attr_accessor :name
# セッターメソッドのみprivateにする
private :name=
def rename_to_bob
# セッターメソッドなら公開レベルにかかわらずself付きで呼び出せる
self.name = 'Bob'
end
end
user = User.new
# エラーにならずにnameを変更できる
user.rename_to_bob
user.name #=> "Bob"
もちろん、name=
はprivateメソッドなのでクラスの外部からは呼び出すことができません。
user.name = "Carol"
#=> NoMethodError: private method `name=' called for #<User:0x00007fdb78827138 @name="Bob">
ただ、このようなコードは混乱を招きやすいので、何か特別な理由がなければセッターメソッドを使わずにインスタンス変数を直接書き換える方がよいでしょう。
class User
# ゲッターメソッドのみ用意して、セッターメソッドは作らない
attr_reader :name
def rename_to_bob
# インスタンス変数を直接書き換える
@name = 'Bob'
end
end
user = User.new
user.rename_to_bob
user.name #=> "Bob"
参考
privateなセッターメソッドがself付きで呼べるようになったのはRuby 1.8からみたいです。
parse.y (attrset)
“self.foo=x” can be legal even when “foo=” is private.
次回予告
次回はself.class
でクラスメソッドを呼び出すときの注意点を説明します。