【Ruby】クラスの作り方覚書
ずーっとC Familyな言語ばっかり使っていたので(VBの話はやめよう)クラスを作るだけでもどうやるのか調べないといけません。
面倒なのでずらずらずらっとメモしていきます。
宣言
ふつーにclassです。
class Hoge end
継承はこんな感じ。
class Hoge < Fuga end
スコープはメソッドにしか適用できないので、privateなclassはないみたい。
また、abstractもサポートされていない。そもそもインタプリタなので、abstract classや(JavaやC#で言う)interfaceで責任を担保することが難しいんだと思う。
コンストラクタはinitializeと言うメソッドを宣言すればOK。こんな長い文字列を打たせるのは多分嫌がらせだと思います。initでよかったじゃん。
class Hoge def initialize end end
メソッド
宣言はclass内で「def [識別子]」とすればOK。(別にclass内じゃなくてもいいけど)
class Hoge def fuga end end
スコープを変えたい場合はこんな感じ。
class Hoge def fuga puts "fuga" end private def piyo puts "piyo" end end obj = Hoge.new obj.fuga obj.piyo #=> NoMethodError
privateの正体はModule#privateであり、これを呼び出すとそれ以降のメソッドは全部privateになるみたい。
引数としてメソッド名のシンボルを可変長で受け取っており、こんなことも出来る。
class Hoge def fuga puts "fuga" end def piyo puts "piyo" end def foo puts "foo" end private :piyo, :foo end obj = Hoge.new obj.fuga obj.piyo #=> NoMethodError obj.foo #=> NoMethodError
この方法だと先に全メソッドを宣言しなきゃいけないので、C Familyに慣れてると凄まじくキモいし、メソッドの量が増えたら可読性も悪そうに思える。普通に各メソッドに書かせてくれればいいのにね。
スコープはこの他にpublicとprotectedがあります。friendはないんですね。まぁ、滅多なことでは使わないんだけど。
ちなみにオーバーロードに該当するものはない。多分、後に宣言したものが優先される。
class Hoge def piyo puts "piyo" end def piyo(s) puts s end end hoge = Hoge.new hoge.piyo #=> ArgumentError hoge.piyo "piyo" #=> piyo
アクセサ
C#のプロパティみたいなものがあります。
class Hoge attr_accessor :fuga attr_reader :piyo attr_writer :foo def initialize @fuga = "fuga"; @piyo = "piyo"; @foo = "foo"; end end hoge = Hoge.new puts hoge.fuga puts hoge.piyo puts hoge.foo #=> NoMethodError hoge.fuga = "fugafuga" hoge.piyo = "piyopiyo" #=> NoMethodError hoge.foo = "foobar"
attr_xxもスコープと同じくModuleのメソッドみたいです。シンボルの可変長引数なので、同時に複数宣言することも可能です。
get/setの挙動をちょっと書き換えたり、インスタンス取得時に初期値を与えたりすることはできません。まぁその辺はC#が優秀すぎるとこはあるんですが。
get/setで値を計算したい場合は以下のように書き換えられます。
class Hoge #getter def fuga return @fuga + "fuga" end #setter def fuga=(val) @fuga = val + "fuga" end end hoge = Hoge.new hoge.fuga = "piyo" puts hoge.fuga #=> piyofugafuga
特異メソッド / 特異クラス / クラスメソッド
これはある特定の1インスタンスにメソッドを定義できると言う中々キモい文法です。
javascriptと同じようにメソッド名とメソッドのハッシュを自由に生やせると思っておけば、多少キモさも薄らぐでしょう。
o = Object.new def o.piyo puts "piyo" end o.piyo #=> piyo
特異クラスと呼ばれる構文を使用すると、同時に複数定義できます。
o = Object.new class << o def piyo puts "piyo" end def fuga puts "fuga" end end o.piyo #=> piyo o.fuga #=> fuga
この特異ほにゃららでのselfが示すオブジェクトは、指定したオブジェクト自身です。なので、アクセサから変数にアクセスしたり、メソッドを呼び出すことが出来ます。
ちょっとわかりにくいですね。つまりこんなことが出来ます。
class Hoge attr_accessor :fuga def initialize @fuga = "fuga"; end def foo(s) puts s end end hoge = Hoge.new hoge.fuga ="piyo" def hoge.new_fuga puts "new" + self.fuga end def hoge.new_foo self.foo "fooooooo" end hoge.new_fuga #=> newpiyo hoge.new_foo #=> fooooooo
これを応用してインスタンスのステートに依存しないメソッドをクラスに定義することができます。C Familyはstaticなメソッドとして宣言しますが、Rubyはクラスメソッドと呼んでいます。別にどっちでもいいんじゃないですかね。
class Hoge def self.fuga puts "fuga" end end obj = Hoge.new obj.fuga #=> NoMethodError Hoge::fuga #=> fuga
クラスメソッドをprivateにするには特異クラス風にクラスメソッドを作る必要があります。
class Hoge class << self def fuga puts "fuga" end def foo piyo end private def piyo puts "piyo" end end end Hoge::fuga #=> fuga Hoge::piyo #=> NoMethodError Hoge::foo #=> piyo
Hoge::fooからはprivateなHoge::piyoが呼べます。同じスコープですからね。
ちなみに、インスタンスメソッドからクラスメソッドを呼ぶ場合は明示的にクラス名を書く必要があります。面倒ですね。
class Hoge class << self def fuga puts "fuga" end end def piyo fuga end end Hoge::fuga #=> fuga hoge = Hoge.new hoge.piyo #=> NameError
class Hoge class << self def fuga puts "fuga" end end def piyo self.fuga end end Hoge::fuga #=> fuga hoge = Hoge.new hoge.piyo #=> NoMethodError
class Hoge class << self def fuga puts "fuga" end end def piyo Hoge::fuga end end Hoge::fuga #=> fuga hoge = Hoge.new hoge.piyo #=> fuga
メソッドのスコープ(privateとprotected)
こう、JavaとかC#とかC++とかやってると、privateは「そのクラスでのみ参照できる(継承したクラスからでもダメ)」で、protectedは「そのクラスかそれを継承したクラスでのみ参照できる」って思うんですが、Rubyは違うみたいです。どうもSmalltalkの文法をベースにしているみたいですね。
うだうだ言うより例を見せたほうが早いでしょう。p_hoge_methodはHogeクラスのprivateなメソッドですが、Hogeを継承したFugaクラスのメソッドからは呼び出せます。
class Hoge private def p_hoge_method puts "this method is private" end end class Fuga < Hoge def hoge_method_wrap p_hoge_method end end fuga = Fuga.new fuga.hoge_method_wrap #=> this method is private fuga.p_hoge_method #=> NoMethodError
また、Overrideも出来ます。
class Hoge private def p_hoge_method puts "this method is private" end end class Fuga < Hoge def p_hoge_method super end end fuga = Fuga.new fuga.p_hoge_method #=> this method is private
と言うわけで、Rubyのprivateはおおよそぼくらのprotectedです。
じゃあRubyのprotectedは何なの?って話になるんですが、これはちょっとリファレンスの説明を読んでみましょうか。
メソッドは public、private、protected の三通りの呼び出し制限を持ちます。
「そのメソッドを持つオブジェクトがselfであるコンテキスト」がキモみたいですね。
とりあえずprivateと同じことをやってみましょう。
class Hoge protected def p_hoge_method puts "this method is protected" end end class Fuga < Hoge def hoge_method_wrap p_hoge_method end end fuga = Fuga.new fuga.hoge_method_wrap #=> this method is protected fuga.p_hoge_method #=> NoMethodError
呼べますね。じゃあ次。
class Hoge protected def p_hoge_method puts "this method is protected" end end class Fuga < Hoge def p_hoge_method super end end fuga = Fuga.new fuga.p_hoge_method #=> this method is protected
呼べますね。じゃあどこで変わるのか?って話なんですが、特異メソッド / 特異クラスの中で変わってきます。
class Hoge protected def protect_hoge puts "this method is protected" end private def private_hoge puts "this method is private" end end hoge = Hoge.new def hoge.protect self.protect_hoge end def hoge.private self.private_hoge end hoge.protect #=> this method is protected hoge.private #=> NoMethodError
とまぁこんな感じに、protectedなメソッドは「そのメソッドを持つオブジェクトがselfであるコンテキスト」である特異メソッドからは呼び出せますが、privateなメソッドは呼び出せません。
逆に言うと、完全にprivateなメソッドはModuleのメソッドでは実現できないと言うことです。
多分抜け道もあるんじゃないかなって思うんですが、あんまり本気で探していません。これはこれでRubyの仕様であり思想であり美学なのでしょう。それぞれのプログラミング言語が持つ美学から無理に外れようとすると、大概面倒なことになるだけですので…。
まとめ
とりあえず自分がばーっとリファレンスを読んでわかりにくかったところだけ。
Mix-inみたいなクラスより上位の話はまた機会があったらまとめておきたいですね。そんな時間はあるのでしょうか。