概要
super は現在のメソッドがオーバーライドしているメソッドを呼び出します。括弧と引数が省略された場合には現在のメソッドの引数がそのまま引き渡されます。引数を渡さずにオーバーライドしたメソッドを呼び出すには super() と括弧を明示します。
このsuper
ですが、以下のようにメソッドチェーンすることで続きの処理を書くことができます。
def some_method
super.to_s
end
詳細
例えば次のような受け取った引数を使って文字列を返すメソッドを持ったクラスKlass
があるとします。
class Klass
def hello(arg: nil)
"Hello #{arg}!"
end
end
klass = Klass.new
result = klass.hello(arg: "World")
puts result
これを実行すると期待通り文字列が出力されます。
% ruby sample.rb
Hello World!
Klass
をSubKlass
で継承し、メソッドhello
をオーバーライドしてみます。
class Klass
def hello(arg: nil)
"Hello #{arg}!"
end
end
class SubKlass < Klass
def hello(arg: nil)
"Hi #{arg}!"
end
end
klass = Klass.new
result = klass.hello(arg: "World")
puts result
sub_klass = SubKlass.new
result = sub_klass.hello(arg: "World")
puts result
SubKlass
でメソッドhello
がオーバーライドできていることが確認できます。
% ruby sample.rb
Hello World!
Hi World!
そして、SubKlass#hello
の中でsuper
と書くと、
SubKlass#hello
が受け取った引数をそのまま使って継承元のKlass#hello
の結果を得ることができます。
class SubKlass < Klass
def hello(arg: nil)
super
end
end
% ruby sample.rb
Hello World!
Hello World!
ではここからが本題です。
super
にメソッドチェーンして、文字列を操作してみます。
(冗長なのでKlass#hello
の呼び出しは削除します)
class Klass
def hello(arg: nil)
"Hello #{arg}!"
end
end
class SubKlass < Klass
def hello(arg: nil)
super.split(" ").reverse.join(" ")
end
end
sub_klass = SubKlass.new
result = sub_klass.hello(arg: "World")
puts result
これを実行すると以下のような結果を得ることができます。
% ruby sample.rb
World! Hello
もちろん、単純に+
で追加の文字列を連結することも可能ですし、string以外の型でも使用できます。
これは、super
の正体がKlass#hello
によって返された値("Hello World!"
)であるがゆえに、
「戻り値のクラスの組み込みメソッドをチェーンすることで処理を追加できる」ということです。
つまりhello
が値を返すものではない(最後の行がputs
など)場合には意図した通りにはなりません。
使用例
この性質が活きると思うのは実行結果がhashを返すメソッドをオーバーライドする場合かなと思います。
例えばRailsの実装で、「基底のコントローラーTypeA
ではリファラーのURLは必要ないけど、TypeA
を継承したコントローラーTypeB
では使いたい」といったような状況があったとき。
class TypeAController < ApplicationController
def create
ResourceA.create(params)
end
def params
{
key_1: params[:key_1],
key_2: params[:key_2]
}
end
end
class TypeBController < TypeAController
def create
ResourceB.create(params)
end
def params
super.merge(additional_params)
# => {key_1=>params[:key_1], key_2=>params[:key_2], referrer=>request.referrer}
end
def additional_params
{ referrer: request.referrer }
end
end
このように、サブクラスで必要になった値を足す、みたいなことができたりします。
あまり見ない書き方ではあるのですが、rubyのsuper
はこのような使い方もできるというお話でした。
(他の言語でもできるのでしょうか)