【Ruby】superしか呼び出していないinitializeメソッドは定義する必要ない
こんにちは、しきゆらです。
今回は、Rubocopの更新を追っていたときに初めて知ったことをメモしておきます。
Rubocopさんは、Rubyを書く方は大半使っているのではないか、と思われる静的コード解析ツールです。
設定により、メソッドの長さやネストの深さなどを制限することで読みやすいコードを書く手伝いをしてくれる頼もしいツールですね。
そんなRubocopさんですが、先日0.90.0がリリースされました。
使っているGemの更新履歴をある程度追っているため、更新内容を確認していたところ今回の更新でも新しい項目が追加されていました。
それが、Lint/UselessMethodDefinition
です。
There are at least 2 cases:
https://github.com/rubocop-hq/rubocop/issues/8472
・empty constructors – this may be just overriding parent’s constructor, but this is bad anyway
・methods just callingsuper
ということで、空のinitialize
メソッドやsuper
しか呼び出していないメソッドを怒ってくれるというものです。
内容的には妥当なcopではないかと思います。
必要ないものは書くべきではないですね。
起こったこと
自分のコードを更新したRubocopにかけたところ、このCopに引っかかりました。
引っかかったコードは以下のようなもの。
class A
def initialize
# 諸々の初期化
end
# 諸々のメソッド定義
end
class B < A
def initialize
super
end
# 諸々のメソッド定義
end
よくある、親クラスを継承してごにょごにょするクラスを作っていました。
その中の、サブクラスのinitialize
メソッドが怒られていました。
確かに、このメソッドは親クラスを呼び出すだけなので怒られるのは分かります。
しかし、私はinitialize
メソッドはサブクラス側で初期化内容を上書きする必要がある場合はもちろんですが、
上書きする必要がない場合でも明示的に親クラスを呼び出す必要があると思い込んでいました。
ただ、クラスBのinitialize
メソッドを削除したとしても意図する挙動をしてくれました。
動くのは分かったのですが、なぜ必要ないのかがわからない。
ということで、色々調べてみました。
一応リファレンスも確認してみましたが、詳しいことは書いていませんでした。
https://docs.ruby-lang.org/ja/latest/method/Object/i/initialize.html
挙動を調べてみる
ということで、古い書籍ですが「プログラミング言語Ruby」を引っ張り出してきました。
内容としてはRuby1.9や2.0あたりなので、だいぶ古いですが根本的には大きく変わっていないので、今回のような場合は安心して使えます。
探してみると、P. 246の脚注にほしい情報がばっちり書かれていました。
該当部分を引用すると、
Rubyでは、initializeは通常のメソッドであり、他のメソッドと同じ様に継承されるのである。
https://amzn.to/3bzluOy
ということで、サブクラス側でinitialize
メソッドをオーバーライドしていない場合は親クラスのinitialize
メソッドがそのまま呼ばれるということでした。
つまり、上記のように明示的に親クラスを呼び出すだけのinitialize
メソッドは必要ない、ということです。
また、super
の記述に関しても引数を渡さない場合のsuper
の挙動は受けた引数をそのまま親クラスのメソッドに渡すように動くようです。
ということで、ただ単に親クラスのメソッドを呼び出すだけのメソッドは本格的に必要がないということがわかりました。
書籍では、以下のようなクラスを定義して説明されていた。
class Point
def initialize(x, y)
@x = x
@y = y
end
def to_s
"(#{@x}, #{@y})"
end
end
class Point3D < Point; end
上記のように定義した場合、Point3D
クラスはPoint
クラスと同じ挙動をします。
point = Point.new(1, 2)
point3d = Point3D.new(2, 3)
point.to_s
#=> (1, 2)
point3d.to_s
#=> (2, 3)
この時、Point3D
側ではinitialize
メソッドが定義されていないが、きちんとPoint
クラスのinitialize
メソッドを呼び出してくれているようです。
これまで、そこそこ長いことRubyさんと戯れてきましたがこれに関してはきちんと把握できていませんでした。
Rubocopの更新を追いかけていたおかげで、これまであいまいになっていた部分の知識を確認する機会を得ることができました。
思わぬところで収穫がありましたが、きちんと中身を追っていくことは大事なんだなぁ、ということを改めて思いました。
まとめ
今回は、Rubocopの更新を追っているうちに自分の知らない挙動を知ることができました。
ただ単に触れているだけではあまり詳しいことを知ることはできないということと、周りにあるコードやライブラリの中身を追っていくのは大事なんだということを改めて思い知りました。
今後もGemの更新を追っていくので、その中で自分が知らなかったことをメモしていきます。
今回は、ここまで。
おわり