ずーっと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みたいなクラスより上位の話はまた機会があったらまとめておきたいですね。そんな時間はあるのでしょうか。