はじめに
この備忘録は、Rubyを勉強していて、気になった処理内容を実際に調査&実行して、それを備忘録として作成したものです。
概要
Rubyで、サブクラスのメソッドでスーパークラスのメソッドを「引数あり・なし」と「デフォルト値あり・なし」で呼び出した場合の動きをまとめました。
今回は一番お世話になるであろう、initializeメソッドさんに協力を依頼しました。
結論を先に書きますと、サブクラスのメソッド内でsuperを実行する場合、
・サブクラスのメソッドの引数は、スーパークラスにも渡る
・引数が設定されていない場合は、値は渡らない。
の2点が分かりました。
開発環境
・Rubyのバージョンは2.3.0
・Rubyのビルダのバージョン?(rbenv)は1.1.1-6
・virtual box にCentOS7をインストールし、実行
備忘録
< 引数なし、デフォルト値なし >
まず、1つ目のパターン。
スーパークラス、サブクラス共にinitializeメソッドに引数およびデフォルト値を設定しない場合の動きです。
確認するソースコードは以下の通りです。
# -*- encoding: utf-8 -*-
class A
attr_reader :x
def initialize
@x = 100
end
end
class B < A
def initialize
@x = 200
super
end
end
b = B.new
p b.x
これの実行結果は、以下の通りになります。
100
この処理では、まずサブクラス(Bクラス)のbというインスタンスを生成するために、サブクラス(Bクラス)のnewメソッドを呼び出しました。
そしてそのnewメソッドがサブクラス(Bクラス)のinitializeメソッドを暗黙的に呼び出しました。
この段階でインスタンス変数xには 200 が設定されています。
さらにそのinitializeメソッドがスーパークラス(Aクラス)のinitializeメソッドを呼び出し、結果的にインスタンス変数xには 100 が設定されました。
これについては特に問題はないですね。では次の例はどうでしょうか。
< 引数あり(スーパークラスのみ)、デフォルト値なし >
# -*- encoding: utf-8 -*-
class A
attr_reader :x
def initialize x
@x = 100
end
end
class B < A
def initialize
@x = 200
super(150)
end
end
b = B.new
p b.x
Sample01.rbとの変更点は、クラスAのinitializeメソッドに引数を追加したことだけです。
これの実行結果は、以下の通りになります。
100
Aクラスのinitializeメソッドには、引数を設定したものの、その値は利用していないので結果は変わりません。
では、次のパターンを動かしてみます。
< 引数あり(サブクラスのみ)、デフォルト値なし >
# -*- encoding: utf-8 -*-
class A
attr_reader :x
def initialize
@x = 100
end
end
class B < A
def initialize x
@x = 200
super
end
end
b = B.new(150)
p b.x
Sample02.rbとの変更点は、スーパークラス(Aクラス)のinitializeメソッドの引数を無くし、サブクラス(Bクラス)に設定したことだけです。
これの実行結果は、以下の通りになります。
Sample03.rb:4:in 'initialize': wrong number or arguments (given 1, excepted 0) (ArgumentError)
from Sample03.rb:12:in 'initialize'
from Sample03.rb:16:in 'new'
from Sample03.rb:16:in '<main>'
エラーになってしまいました。エラー内容から察するに、サブクラス(Bクラス)のinitializeメソッドが、superを呼び出すときに、引数を暗黙的に渡そうとしているみたいです。
ですが、スーパークラス(Aクラス)のinitializeメソッドには引数の設定がされていないため、エラーになったみたいです。
エラー内容を踏まえて、スーパークラス(Aクラス)のinitializeメソッドに引数を設定して実行してみました。
なお、superには引数は設定しません。
# -*- encoding: utf-8 -*-
class A
attr_reader :x
def initialize x
@x = 100
end
end
class B < A
def initialize x
@x = 200
super
end
end
b = B.new(150)
p b.x
実行結果は以下の通りです。
100
このことから、サブクラス(Bクラス)のinitializeメソッドに引数が設定されると、superには暗黙的に引数が設定されていることが分かります。
Rubyにはオーバーロードの概念は基本的に無いようなので、この結果も納得です。
ついでに、initializeメソッドにデフォルト値が設定されていている場合も見てみましょう。
今度は、スーパークラス(Aクラス)、サブクラス(Bクラス)のinitializeメソッドに引数ありで、更にデフォルト値を設定した場合です。
< 引数あり、デフォルト値あり >
# -*- encoding: utf-8 -*-
class A
attr_reader :x
def initialize(x=100)
@x = x*3
end
end
class B < A
def initialize(x=200)
@x = x*2
super
end
end
b = B.new
p b.x
どのように処理されたのか分かりやすいように、それぞれのinitializeメソッドでは乗算を行うようにしています。
実行結果は以下の通りです。
600
引数が設定されていないので、サブクラス(Bクラス)のデフォルト値 200 が、super実行時に暗黙的に設定されます。
そうするとスーパークラス(Aクラス)のinitializeメソッドでは 300 という値に 3 を掛けているので、 600 が返ってきました。
(ちなみにスーパークラス(Aクラス)のinitializeメソッドに引数を設定していないと、ruby:Sample03.rbの時と同じエラーが発生します。)
なお、newメソッドに引数を設定すると、その設定した値がsuperの引数に暗黙的に設定されます。
これも例を見てみましょう。
< 引数あり、デフォルト値あり >
# -*- encoding: utf-8 -*-
class A
attr_reader :x
def initialize(x=100)
@x = x*3
end
end
class B < A
def initialize(x=200)
@x = x*2
super
end
end
b = B.new(500)
p b.x
実行結果は以下の通りです。
1500
サブクラス(Bクラス)のnewメソッドの引数に設定した 500 の値がスーパークラス(Aクラス)に渡って計算されていますね。
まとめ
サブクラスのメソッド内でsuperを実行する場合、
・サブクラスのメソッドの引数は、スーパークラスにも渡る
・引数が設定されていない場合は、値は渡らない。
と言えます。