より正確には、サブクラスのインスタンスから、自身が属するクラスがオーバーライドしたスーパークラスのメソッドを、自身が属するクラスに定義した別のメソッドからコールしたいときにはどうするか?…でしょうか。(^_^;)
2chの Ruby 初心者用スレへの投稿 より。具体的には、次の D#bar で B#foo を呼びたいときの対処法について。以前から漠然と、Smalltalk の super と違う Ruby の super の仕様(こういう仕様の言語ってほとんどないですよね。もしや Ruby のオリジナル? 追記:よく似たものに Eiffel の Precursor というのがありました)だと、いろいろと問題があるんじゃないかなぁ…と思っていたところなので、そのよい例としてメモ。
class B def foo; "B#foo" end end class D<B def foo; "D#foo" end def bar # どうする? end end
Smalltalk では super は self と同様のしかし特殊な振る舞い(メソッド検索だけは、実行中のメソッドの属するクラスのスーパークラスから始める…)をする擬変数なので、このような場合でも普通に super foo と書くことができます。(以下、メタ情報を除いた加工コード(ファイルイン不可。ブラウザへのコピペが必要)にて)
Object subclass: #B B >> foo ^ 'B >> #foo' B subclass: #D D >> foo ^ 'D >> #foo' D >> bar ^ super foo
D new bar " => 'B >> #foo' "
一方、Ruby の super は、Smalltalk の super のような擬変数(メッセージレシーバ、あるいは、ドットアクセス演算子の第一オペランドとして使用される…)ではなく、スーパークラスの同名メソッドの エイリアスなので 別名メソッドがごとく(たとえるなら、super という名前に alias した後、private 属性を付したかのように…)振る舞うので、こうしたケースには役に立ちません。そこで、二つの解決策が提示されています。(「エイリアスなので」という表現には語弊があるので緑色部分として書き換え、書き足しました。rubyco さん、ご指摘、痛み入ります。)
エイリアスを用いる alias で別名を作る方法
class D1 < B alias :super_foo :foo def foo; "D#foo" end def bar super_foo end end
D1.new.bar #=> "B#foo"
メソッドオブジェクトを引っこ抜いて直接叩く方法
class D2 < B def foo; "D#foo" end def bar self.class.superclass.instance_method(:foo).bind(self).call end end
D2.new.bar #=> "B#foo"
前者は D1#foo の定義前に alias しておくというのがミソですね(というか、alias の使用パターン?)。後者は Python や JavaScript チックな発想ですね。Squeak の Smalltalk で無理矢理表現するなら、
D >> bar2 ^ (self class superclass lookupSelector: #foo) valueWithReceiver: self arguments: #()
D new bar2 " => 'B >> #foo' "
という感じでしょうか。
id:sumim:20061203:p1 へ続く。
ところで、これは先に述べた Smalltalk における super の仕様から、次のように書いてもよさそうに思います。しかし残念ながら、この記述では期待どおりの動作をしません。
D >> bar3 ^ (super class lookupSelector: #foo) valueWithReceiver: self arguments: #()
D new bar3 " => 'D >> #foo' "
理由は、Squeak のバイトコードインタープリタが、メッセージ「class」を実際には送信していないからです。ちょっとした衝撃の事実ですね(^_^;)。その代わり、この「class」に限っては常にハードコードされたバイトコード(16rC7。インタプリタ内では #bytecodePrimClass)を直接コールすることになっています。同様の理由で、#class はオーバーライドすることもできないので、注意が必要でしょう。こうした例外には #class の他に、#==(バイトコード 16rC6。同、#bytecodePrimEquivalent)があります。