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] - [ルートメニュー]