ぎょーぼのぶろぐ

IT系の話を書いていくブログです。今はRubyの勉強中。

【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 例外

protectedprivate の差は、サブクラスや他のクラスのインスタンスメソッド内で使えるかどうか、になります。サンプルでは、クラス 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に続きます。