衝撃を受けたできごと
最近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先生に設計思想を聞いてみたら回答してもらえました〜!うれしいです。
@yukihiro_matz さん、こんなブログ( http://t.co/JOUkJTJm )を書いたんですが、もう少し掘り下げたいので質問させて下さい。privateメソッドをサブクラスから呼べるようにした設計思想を知りたいです。またこういう仕様を持つ言語は他にもありますか?
— Junichi Ito (伊藤淳一) (@jnchito) March 14, 2012
@JunichiIto77 privateという名前がJavaやC++に馴染んだ人に誤解を生みやすいのは想定外でした。Rubyを最初に設計した頃にはJavaは一般公開されてませんでしたし、private(など)はC++固有の機能でしたから尊重しませんでした
— Yukihiro Matsumoto (@yukihiro_matz) March 15, 2012
@JunichiIto77 Rubyのprivateの発想の元になったのはSmalltalkの「privateカテゴリ」です。使わないでね、というだけでアクセスできちゃう。Rubyはそれよりは若干強制力があります。Rubyの反C++・親Smalltalkの設計思想が垣間見えますね
— Yukihiro Matsumoto (@yukihiro_matz) March 15, 2012
@JunichiIto77 後でprotectedを追加したのもまずかった。これでC++とキーワードが同じでも意味がズレてることになってしまったので。
— Yukihiro Matsumoto (@yukihiro_matz) March 15, 2012
今だったらどんなキーワードにするのか気になるところ。 QT @yukihiro_matz http://t.co/ro2kFIHh
— あおい (@aoi0308) March 15, 2012
@aoi0308 今だったら? protected入れない。
— Yukihiro Matsumoto (@yukihiro_matz) March 15, 2012
なるほど〜。Smalltalkの影響ですか。
確かにRubyが誕生したころはJavaも知られてなかったですもんね。
やはり歴史や思想を確認すると、より理解が深まります。
どうもご回答ありがとうございました!
参考書籍
- 作者: まつもとゆきひろ,David Flanagan,卜部昌平(監訳),長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2009/01/26
- メディア: 大型本
- 購入: 21人 クリック: 356回
- この商品を含むブログ (129件) を見る
あわせて読みたい
英語ブログを書いてredditに投稿してみた - give IT a try
このエントリを英語ブログに翻訳した時の一部始終を紹介しています。
Rubyのクラスメソッドは同じクラスのprotectedメソッドやprivateメソッドにアクセスできない - give IT a try
これもC#/Javaプログラマが意外に思うようなRubyの言語仕様です。