読者です 読者をやめる 読者になる 読者になる

give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

JavaやC#の常識が通用しないRubyのprivateメソッド

Ruby OOP

衝撃を受けたできごと

最近Rubyを勉強しています。
JavaやC#でオブジェクト指向プログラミングの基本はマスターしてるから、Rubyもそのあたりは楽勝〜!・・・と思っていたら、JavaやC#の常識が全く通用しない振る舞いに遭遇してかなり衝撃を受けました。それは、


privateメソッドはサブクラスからも呼び出せる


・・・ということです!!がーん。


たとえば、JavaやC#だと自分のクラス内でprivateメソッドが使われていない場合、不要なメソッドとして削除できます。(リフレクションを使って呼び出される可能性はここでは無視ね)
しかし、Rubyでは誰かがサブクラスを作って呼び出している可能性があるので、privateメソッドを削除する場合は注意が必要です。メソッド名を変更する場合も同様ですね。


また、知らずに親クラスと同名のprivateメソッドを定義すると、予期せず親クラスの実装をオーバーライドしてしまうことにもなります。
なので、Rubyの教科書には継承を使う時は十分注意せよと書いてありました。


じゃあ、protectedメソッドはなんやねん!?ということになりますが、これはサブクラスがインスタンス経由で呼び出せる親クラスのメソッドを表します。
言い換えるとprivateメソッドの場合、サブクラスはインスタンス経由で親クラスのprivateメソッドを呼び出すことはできません。


だんだん話がややこしくなってきたので、コードで確認してみましょう。

サンプルコード

親クラスHogeとサブクラスPiyoを作って色々と実験してみました。
解説は実行結果の後に載せています。
ただ、ちょっと長いのでWebだと一画面に収まらないかもしれません。見にくくてごめんなさい。

親クラスの定義
class Hoge
  protected
  def a_protected_method
    "PROTECTED! - #{do_not_override_me}"
  end
 
  private
  def a_private_method
    "PRIVATE! - #{do_not_override_me}"
  end
 
  #May be overridden by sub class
  def do_not_override_me
    "I am Hoge."
  end
end
サブクラスの定義
class Piyo < Hoge
  def call_a_protected_method
    a_protected_method
  end
 
  def call_a_private_method
    a_private_method #OK!!
  end
 
  def call_a_protected_method_via(hoge_or_piyo)
    hoge_or_piyo.a_protected_method
  end
 
  def call_a_private_method_via(hoge_or_piyo)
    hoge_or_piyo.a_private_method #NG!!
  end
  
  private
  # Override a private method in super class and called from super class
  def do_not_override_me
    "I am Piyo."
  end
end
実行用コード
def puts_safe
  puts yield
rescue => e
  puts "ERROR!! - #{e}"
end

hoge = Hoge.new
piyo = Piyo.new

puts "<<Call a method in super class>>"
puts_safe { piyo.call_a_protected_method }
puts_safe { piyo.call_a_private_method } #OK!!
puts "\n"

puts "<<Call a method via the instance of super class>>"
puts_safe { piyo.call_a_protected_method_via hoge }
puts_safe { piyo.call_a_private_method_via hoge } #ERROR!!
puts "\n"

puts "<<Call a method via the instance of sub class>>"
piyo2 = Piyo.new
puts_safe { piyo.call_a_protected_method_via piyo2 }
puts_safe { piyo.call_a_private_method_via piyo2 } #ERROR!!
実行結果
<<Call a method in super class>>
PROTECTED! - I am Piyo.
PRIVATE! - I am Piyo.

<<Call a method via the instance of super class>>
PROTECTED! - I am Hoge.
ERROR!! - private method `a_private_method' called for #<Hoge:0x9c74814>

<<Call a method via the instance of sub class>>
PROTECTED! - I am Piyo.
ERROR!! - private method `a_private_method' called for #<Piyo:0x9c745d0>
解説

「a_private_method」というprivateメソッドが親クラスHogeにあって、そのメソッドをサブクラスPiyoから呼び出しています。
サブクラスPiyoでは単純に親のメソッドをコールする場合と、メソッドに渡されたインスタンス経由でコールする場合の2パターンを作りました。
実行結果を見ると、前者(call_a_private_method)は呼び出しに成功していますが、後者(call_a_private_method_via)は失敗しています。


また、親クラスHogeで「do_not_override_me(いいか、絶対俺をオーバーライドするなよ)」というprivateメソッドを作ったにも関わらず、サブクラスPiyoではバッチリオーバーライドできています。(出力結果で"I am Hoge."と"I am Piyo."が状況によって切り替わっている点に注目)

今回の件で学んだこと

というわけで、どの言語でもたぶん同じだろうとタカをくくっていると、意外な落とし穴にはまってしまいます。
新しい言語を学ぶ時はそれまでの常識が通用しない部分に十分注意する必要があるなあと反省しました。
みなさんも注意してくださいね。

追記(2012.03.15 09:15)

思い切ってRubyのパパこと、Matz先生に設計思想を聞いてみたら回答してもらえました〜!うれしいです。

なるほど〜。Smalltalkの影響ですか。
確かにRubyが誕生したころはJavaも知られてなかったですもんね。
やはり歴史や思想を確認すると、より理解が深まります。
どうもご回答ありがとうございました!

参考書籍

プログラミング言語 Ruby

プログラミング言語 Ruby

  • 作者: まつもとゆきひろ,David Flanagan,卜部昌平(監訳),長尾高弘
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2009/01/26
  • メディア: 大型本
  • 購入: 21人 クリック: 356回
  • この商品を含むブログ (129件) を見る

あわせて読みたい

英語ブログを書いてredditに投稿してみた - give IT a try
このエントリを英語ブログに翻訳した時の一部始終を紹介しています。


Rubyのクラスメソッドは同じクラスのprotectedメソッドやprivateメソッドにアクセスできない - give IT a try
これもC#/Javaプログラマが意外に思うようなRubyの言語仕様です。