読者です 読者をやめる 読者になる 読者になる

Memento memo.

Today I Learned.

Effective Ruby 第二章 クラス、オブジェクト、モジュール まとめ その1

Ruby EffectiveRuby

いつぞやの続きです。

shotat.hateblo.jp

Effective Ruby

Effective Ruby

一記事一章にまとめようとしたところ二章目から量が多くて詰んだので適当なところで切ることにしました。

Effective Ruby 第二章のテーマは"クラス、オブジェクト、モジュール"です。

Rubyは純粋オブジェクト指向言語とも呼ばれていて、全てがオブジェクトです。クラス、数値リテラルを含め全てオブジェクトです。クラス自体がオブジェクトなのです。

用語

  • オブジェクト: 変数の入れ物(※)
  • インスタンス変数: オブジェクトに状態を表現する変数
  • インスタンス: 特定のクラスの実例
  • クラス: メソッドと定数の入れ物
  • インスタンスメソッド: クラスのインスタンス(オブジェクト)のふるまいを表現する
  • クラス変数: クラスに格納された変数
  • クラスオブジェクト: (クラス自体がオブジェクト)
  • クラスメソッド: クラスオブジェクトのメソッド
  • スーパークラス: クラス階層内の親クラスの別名
  • モジュール: 制限付きクラス(newとかがない)
  • 特異クラス(singleton class): クラス階層に現れる不可視クラス。全てのオブジェクトが一つ持つ。
  • 特異メソッド(singleton methods: 特異クラスに格納される、クラスメソッドや特定オブジェクト専用メソッド
  • レシーバ: メソッドが呼び出されるオブジェクト

(※)オブジェクトが変数の入れ物っていうのはちょっと違うような気が。 少なくとも変数と振る舞いを両方カプセル化しています。

Rubyの継承階層について

  • オブジェクトは継承階層を使ってメソッドを探す
  • 階層ルートまで辿ってもメソッドが見つからなければ method_missing メソッドを探す
  • includeメソッドでmoduleをmixinした場合、特異クラス(不可視)がクラス階層に挿入される
  • singleton_classメソッドはレシーバのための特異クラスを返す
  • singleton_methodsメソッドは特異クラスに格納された特異メソッドを返すメソッド

コード書いて実験するのがわかりやすいですね。

class Hoge
  def foo
    puts 'bar'
  end
end

hoge0 = Hoge.new
hoge1 = Hoge.new
hoge2 = Hoge.new
def hoge2.foo
  puts 'piyo'
end

hoge1.foo
#=> bar
hoge2.foo
#=> piyo

# 特異クラス
p hoge0.singleton_class
#=> #<Class:#<Hoge:0x007fa93d81c480>>
p hoge1.singleton_class
#=> #<Class:#<Hoge:0x007fa93d81c458>>
p hoge2.singleton_class
#=> #<Class:#<Hoge:0x007fa93d81c430>>
### 全部違う(それはそう)

# 特異メソッド
p hoge1.singleton_methods
#=> []
p hoge2.singleton_methods
#=> [:foo]

superのふるまいが複数あることに注意する

  • 継承階層の上位メソッドをオーバーライドする際はsuperキーワードで上位メソッドを呼び出せる
  • 引数・括弧なしでsuperを呼ぶと呼び出し元のメソッドに渡された全ての引数を渡してオーバーライドされるメソッドの呼び出しをするのと同じ。暗黙的な挙動のため注意。
  • 引数を渡したくない場合は super() のように括弧をつける

基本的に括弧つけた方がよさげです。

サブクラスの初期化にはsuperを呼び出す

  • Rubyのサブクラスのinitializeメソッドは自動的にスーパークラスのinitializeのメソッドを呼び出さない。
  • initializeメソッドも通常のメソッドルックアップ規則が当てはまる(継承階層をたどる)
  • 明示的にinitializeメソッドを定義するときはinitializeメソッド内でsuperを呼ぶ。initialize_copyとかでも同様。

紛らわしい構文に注意

  • Rubyはメソッド名の末尾に "?", "!", "=" 等が使えるが、 "=" は構文的に別なので注意(セッターメソッド)
  • 代入と異なり、セッターメソッドは明示的なレシーバが必要
hoge = 1 # 代入
foo.bar = 2 # セッターメソッド呼び出し
self.a = 3 # セッターメソッド呼び出し
  • セッターメソッド以外は明示的にレシーバ指定しなくてOKなので、不要なselfを連発してはいけない。

続きます。