Ruby
TIPS 〜 疾風怒濤編
[SOFTWARE] - [SOFTWARE.ruby] - [Ruby] - [ルートメニュー]
alias
- 継承クラスのコンストラクタ
- String 継承の落とし穴
- 定義すべきメソッド
alias は「指定されたシンボルそのもの」を示すようになります。というわけで、継承などと組み合わせた場合に予想を裏切る (自分が間違えてるだけなんですが) 事態になることがあります。
class A
def a ()
p "A::a"
end
alias b a
def c ()
a
end
end
class B < A
def a ()
p "B::a"
end
end
b = B.new
b.a()
b.b()
b.c()
このスクリプトの実行結果は次のようになります。
"B::a"
"A::a"
"B::a"
クラス A のメソッド a() を継承クラス B で再定義していますけれども、クラス A のメソッド b() は A::a() の alias として定義されていますので、B::b() は A::a() を呼び出すことになります。一方 A::c() は内部でメソッド a() すなわち self.a() を呼び出していて、これはクラス B のコンテキストで実行されると B::a() を呼び出すことになります。
ということで、継承も考えている場合は alias ではなく def でちゃんとメソッドを定義した方が安全です。
| 2. 継承クラスのコンストラクタ
|
| (確認) ruby 1.6.4
|
たとえば Array クラスに何か機能を付加したいときを考えます。なるべく Array クラスと同じように扱いたいとすれば、Array を継承した新しいクラスを作ってしまうのがいちばん手っ取り早い方法です。これならば Array の持つメソッド、機能をすべて使うことができます。しかし、ここで問題になるのがコンストラクタにちょっとしたコードを入れなければならないという状況の場合です。
次の例を見てください。
class ArrayPlus < Array
def initialize ( len )
super( len )
@database = Hash.new()
end
end
このとき、Array.new( len, [] ) と同じように書こうとしても、ArrayPlus.new( len, [] ) は引数の数が異なるというエラーになってしまいます。それならば initialize ( len, init ) のような宣言にすれば回避することはできますが、これが Array クラスではなく自分で作ったクラスなど、インターフェイスが変わる可能性のあるクラスを継承している場合には、元のクラスを書き換えるたびに継承したもの全てを書き換えなければならなくなってしまいます。
そこで、可変長引数を使って次のような宣言にします。
class ArrayPlus < Array
def initialize ( *arg )
super( *arg )
@database = Hash.new()
end
end
これならば継承元クラスにすべての処理を任せることができ、インターフェイスの変更があっても気にする必要がなくなります。
| 3. String 継承の落とし穴
|
| (確認) ruby 1.6.4
|
String クラスのメソッド、to_s は「self を返す」という定義になっています。また、Array クラスのメソッド、join は「最初のオブジェクトのコピーに以降のオブジェクトを加えていく」という方式をとっています。
そんなわけですので、String を継承したクラスを使っていて、そのクラスに上にあげたメソッドが関わってくるような場面では気をつけなければなりません。
まず、文字列オブジェクトを持ってこようとして to_s を呼び出しても、返ってくるのは継承先クラスのオブジェクトです。返ってきたオブジェクトに inspect などを適用している場合、どうやっても文字列形式の結果が出力されず「おかしいな?」と思ってしまうことがあります。たとえば a.to_s.inspect と書いても、期待するような文字列が得られないことがあるということです。なぜならば、a.to_s.class != String なのですから。
また、継承先クラスのオブジェクトを格納した Array に対して join を適用すると、返ってくるのが継承先クラスのオブジェクトという事態があります。これは前述したとおり、最初のオブジェクトに加えていく、という方針をとっているからです。これまた、a.join('').class != String という事になります。
では必ず文字列オブジェクトが欲しい! という場合はどうするのかというと、普通に String オブジェクトを新たに作成します。これで OK です。
String.new( something.join(':') )
| 4. 定義すべきメソッド
|
| (確認) ruby 1.6.4
|
Ruby のシステムにおいては、実は他のメソッドで実装されているというメソッドが少なくありません。たとえば、print メソッドは内部で write メソッドを呼び出していますし、to_s を定義しておけば join などを適用するとき、文字列を抽出するのに自動的に呼び出されます。
ここでは実質的なメソッドを並べ、新しいクラスを作るとき、どういうメソッドをオーバーライドすればいいのか、わかりやすくしようと思います。
(随時更新します)
| メソッド名 | 使われているメソッド
|
| IO#print | IO#write
|
| IO#printf
|
| IO#p
|
| IO#puts
|
[SOFTWARE] - [SOFTWARE.ruby] - [Ruby] - [ルートメニュー]