【Ruby v2.6】インスタンスメソッド その2(public, protected, private)
前置き
Ruby のインスタンスメソッドについてまとめます。 その2は、public, protected, private について
書き方
クラス定義内で、デフォルトでインスタンスメソッドを定義すると、public のインスタンスメソッドとなります。
インスタンスメソッドの定義をprivate
の後に記述すると private, protected
の後に記述すると protected のインスタンスメソッドになります。
public, protected, private の意味
Ruby での protected, private は、Javaなどのprotected, private とは少し動作が異なります。
public のインスタンスメソッド
制限なく呼び出せるメソッドです。[インスタンス].[メソッド名]で呼び出せます。
protected のインスタンスメソッド
そのクラスのインスタンスメソッドか、サブクラスで定義したインスタンスメソッド内であれば呼び出せるメソッドです。 一番分かりにくいので、サンプルで詳しく説明します。
private のインスタンスメソッド
レシーバーを付けて呼び出すことができないインスタンスメソッドです。[インスタンス].[メソッド名] では呼び出せません。self.[メソッド名]でもダメで、[メソッド名]だけで呼び出します。 この制限のため、同じクラスのインスタンスメソッド以外から呼び出しができないメソッドということになります。
サンプル
以下、サンプルのクラスです。
class A # デフォルトでは public def a_public_method p "called a_method." # protected メソッドの呼び出し self.a_protected_method # private メソッドの呼び出し(self等のレシーバーは付けられない。) a_private_method end protected # ここ以降は protected のメソッドになる # protected メソッド def a_protected_method p "called a_protected_method." end private # ここ以降は private のメソッドになる # private メソッド def a_private_method p "called a_private_method." end end # A のサブクラス B class B < A # A のインスタンスからprotectedメソッドを呼び出す def call_a_protected_method obj = A.new obj.a_protected_method end # A のインスタンスからprivateメソッドを呼び出す def call_a_private_method obj = A.new obj.a_private_method end end # A とは無関係のクラス C class C # A のインスタンスからprotectedメソッドを呼び出す def call_a_protected_method obj = A.new obj.a_protected_method end # A のインスタンスからprivateメソッドを呼び出す def call_a_private_method obj = A.new obj.a_private_method end end
irb でクラスを読み込んで、確認してみます。
クラス A のインスタンスを作成し、a_public_method
を呼び出してみます。
obj_a = A.new obj_a.a_public_method # => "called a_method." # => "called a_protected_method." # => "called a_private_method."
obj_a
のインスタンスメソッドから、protected, private のメソッドが呼び出せています。しかし、インスタンスメソッドの外からprotected, private メソッドを呼び出そうとすると、NoMethodError例外が発生します。
obj_a.a_protected_method # => NoMethodError 例外 obj_a.a_private_method # => NoMethodError 例外
protected
と private
の差は、サブクラスや他のクラスのインスタンスメソッド内で使えるかどうか、になります。サンプルでは、クラス B は クラス A のサブクラス、クラス C はクラス A と継承関係のないクラスで、中身は全く同じです。インスタンスメソッド内でクラスAのインスタンスオブジェクトを生成して、protected, private メソッドへアクセスできるか確かめます。
obj_b = B.new obj_b.call_a_protected_method # => "called a_protected_method." obj_b.call_a_private_method # => NoMethodError 例外 obj_c = C.new obj_c.call_a_protected_method # => NoMethodError 例外 obj_c.call_a_private_method # => NoMethodError 例外
クラス B のインスタンスメソッドからクラスA のインスタンス生成→protectedメソッド読み出しだけが呼び出せています。他はNoMethodError 例外で呼び出せません。
使い分け方
特に理由がなければ、デフォルトの public で問題ありません。インスタンス内部用に、関数的に使う場合や、外から実行しても意味が無い、都合が悪い場合は、private にして制限すると良いと思います。
使いどころが難しいのが protected です。実際、あまり使う機会がなさそう。 同じクラスのオブジェクト間で、相互にメソッドを実行する必要がある場合、他の無関係なクラスのインスタンスメソッドから実行されると困る、誤って実行されるのを防止する目的ならありそうですが、どういうシチュエーションか、あまり想像できないところです。。。
その2のまとめ
- インスタンスメソッドは、public, protected, private の3種類ある。
- 通常の定義では public となり、
protected
の後に定義すると protected メソッド、private
の後に定義すると private メソッドになる。 - public なインスタンスメソッドは、制限なく呼び出しができる。
- protected なインスタンスメソッドは、そのクラスのインスタンスメソッド、またはサブクラスのインスタンスメソッドから呼び出しができる。
- private なインスタンスメソッドは、レシーバーを付けて呼び出しができない。このため、そのクラスのインスタンスメソッド以外からは呼び出せない。
その3に続きます。