Help us understand the problem. What is going on with this article?

privateメソッドをレシーバ付きで呼び出せるケース

More than 1 year has passed since last update.

はじめに

この記事は書籍「プロを目指す人のための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からみたいです。

https://docs.ruby-lang.org/en/2.4.0/ChangeLog-1_8_0.html

parse.y (attrset)
“self.foo=x” can be legal even when “foo=” is private.

次回予告

次回はself.classでクラスメソッドを呼び出すときの注意点を説明します。

jnchito
SIer、社内SEを経て、ソニックガーデンに合流したプログラマ。 「プロを目指す人のためのRuby入門」の著者。 http://gihyo.jp/book/2017/978-4-7741-9397-7 および「Everyday Rails - RSpecによるRailsテスト入門」の翻訳者。 https://leanpub.com/everydayrailsrspec-jp
https://blog.jnito.com/
sonicgarden
「お客様に無駄遣いをさせない受託開発」と「習慣を変えるソフトウェアのサービス」に取り組んでいるソフトウェア企業
http://www.sonicgarden.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした