継承/オーバーライド/super について
rubyで「継承」について勉強しているときに、スーパークラス(親クラス)とサブクラス(子クラス)で同じメソッド名を定義したらサブクラスのメソッドが優先し使用されたので、スーパークラスのメソッドを実行する方法はないか気になったので調べてみました。
継承
複数クラスで使用される共通部分のメソッドをスーパークラス(親クラス、継承元)で定義しサブクラス(子クラス、継承先)でも使用できるようにすること。
継承するとサブクラスでスーパークラスのメソッドを使用したい時でもサブクラスで定義し直すことなく、サブクラス(子クラス、継承先)ではスーパークラスで定義していないメソッドのみ作成すれば良く、コード量を減らすことができる。
定義方法は以下の通り。
class サブクラス名 < スーパークラス名
def 〇〇
~
end
end
rubyでは、サブクラス(子クラス)がスーパークラス(親クラス)を継承している状態で異なる名前のメソッドを定義していれば、サブクラスのインスタンスに対してスーパークラスのメソッドを実行することができる。
class Job1
def myjob_job1
puts "私の仕事は教師です。"
end
end
class Job2 < Job1
def myjob
puts "私の仕事はコメディアンです。"
end
end
job = Job2.new # Job2のインスタンスを作成
job.myjob_job1 # => 私の仕事は教師です。(Job1のmyjob_job1メソッドの実行結果)
ただし、継承してもサブクラス(子クラス、Job2のこと)にスーパークラス(親クラス、Job1のこと)と同じメソッド名のメソッドを定義し直し実行すると、下記のようになる。
class Job1
def myjob
puts "私の仕事は教師です。"
end
end
class Job2 < Job1
def myjob
puts "私の仕事はコメディアンです。"
end
end
job = Job2.new # Job2のインスタンスを作成
job.myjob # => 私の仕事はコメディアンです。(継承してもJob1のmyjobメソッドが使用されず、Job2のmyjobメソッドが使用された。)
そこでrubyでは「オーバライド」をさせることでスーパークラスとサブクラスで同じ名前のメソッドが定義されていても、スーパークラスのメソッドを使用することができるようになる。
オーバーライド
サブクラスの中でスーパークラスで定義されているメソッドと同じ名前のメソッドを定義すること。
スーパークラス(継承元)のメソッド名とサブクラス(継承先)のメソッド名が同じとき、サブクラス(継承先)で定義したメソッドが使用される。
rubyでは「super」というメソッドを用いてスーパークラスで定義したメソッドを呼ぶことができる。
参考
superを使用すると、サブクラスとスーパークラスに同じ名前のメソッドが定義されていても、スーパークラスのメソッドを優先し使用することができる。
class Job1
def myjob
puts "私の仕事は教師です。"
end
end
class Job2 < Job1
def myjob
super
puts "私の仕事はコメディアンです。"
end
end
job = Job2.new
job.myjob
# => 私の仕事は教師です。 (superの実行結果、つまり、継承元のJob1のmyjobメソッドの実行結果)
# => 私の仕事はコメディアンです。 (Job2のmyjobメソッドの実行結果)
superに引数を渡したときは、少しわかりにくいので注意。
# super は現在のメソッドがオーバーライドしているメソッドを呼び出します。
# 括弧と引数が省略された場合には現在のメソッドの引数がそのまま引き渡されます。
# 引数を渡さずにオーバーライドしたメソッドを呼び出すには super() と括弧を明示します。
class Foo
def foo(arg=nil)
p arg
end
end
class Bar < Foo
def foo(arg)
super(5) # 5 を引数にして呼び出す
super(arg) # 5 を引数にして呼び出す
super # 5 を引数にして呼び出す super(arg) の略記法
arg = 1
super # 1 を引数にして呼び出す super(arg) の略記法
super() # 引数なしで呼び出す
end
end
Bar.new.foo 5 # 少しわかりにくいが、Bar.new.foo(5)と同じ。
# => 5 (super(5) の実行結果)
# => 5 (super(arg)の実行結果)
# => 5 (1つ目のsuperの実行結果)
# => 1 (2つ目のsuperの実行結果)
# => nil (super()の実行結果)
Discussion