概要
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はこのような使い方もできるというお話でした。
(他の言語でもできるのでしょうか)