0
0

More than 1 year has passed since last update.

Rubyのsuperはメソッドチェインできる

Posted at

概要

super は現在のメソッドがオーバーライドしているメソッドを呼び出します。括弧と引数が省略された場合には現在のメソッドの引数がそのまま引き渡されます。引数を渡さずにオーバーライドしたメソッドを呼び出すには super() と括弧を明示します。

このsuperですが、以下のようにメソッドチェーンすることで続きの処理を書くことができます。

def some_method
  super.to_s
end

詳細

例えば次のような受け取った引数を使って文字列を返すメソッドを持ったクラスKlassがあるとします。

sample.rb
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!

KlassSubKlassで継承し、メソッドhelloをオーバーライドしてみます。

sample.rb
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の呼び出しは削除します)

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

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0